ListView
動的データを追加/削除した後にAndroidを更新するにはどうすればよいですか?
25 に答える
そのアダプタのデータを変更したら、オブジェクトを呼び出しnotifyDataSetChanged()
ます。Adapter
いつどのように電話をかけるかについてのいくつかの追加の詳細は、このGoogle I/OビデオnotifyDataSetChanged()
で見ることができます。
また、これを使用することができます:
myListView.invalidateViews();
この質問に対するすべての、、、...のinvalidate()
回答は無視してください。invalidateViews()
requestLayout()
正しいこと(そして幸いにも正しい答えとしてマークされていること)は、アダプターを呼び出すことnotifyDataSetChanged()
です。
トラブルシューティング
呼び出しが機能しない場合notifyDataSetChanged()
は、すべてのレイアウトメソッドも役に立ちません。私を信じて、ListView
正しく更新されました。違いが見つからない場合は、アダプタのデータがどこから来ているかを確認する必要があります。
これがメモリに保持している単なるコレクションである場合は、を呼び出す前に、コレクションからアイテムを実際に削除または追加したことを確認してくださいnotifyDataSetChanged()
。
データベースまたはサービスバックエンドを使用している場合は、を呼び出す前に、メソッドを呼び出して情報を再度取得する(またはメモリ内のデータを操作する)必要がありnotifyDataSetChanged()
ます。
これはnotifyDataSetChanged
、データセットが変更された場合にのみ機能します。ですから、そこから変化が見られないかどうかを確認する場所です。必要に応じてデバッグします。
ArrayAdapterとBaseAdapter
BaseAdapterのように、コレクションを管理できるアダプターを使用すると、より適切に機能することがわかりました。ArrayAdapterなどの一部のアダプターは、既に独自のコレクションを管理しているため、更新のために適切なコレクションにアクセスするのが困難です。ほとんどの場合、それは実際には不必要な余分な難しさの層にすぎません。
UIスレッド
これはUIスレッドから呼び出す必要があるのは事実です。他の回答には、これを達成する方法の例があります。ただし、これは、UIスレッドの外部からこの情報を処理している場合にのみ必要です。これは、サービスまたは非UIスレッドからのものです。単純なケースでは、ボタンのクリックまたは別のアクティビティ/フラグメントからデータを更新します。したがって、まだUIスレッド内にあります。そのrunOnUiTrheadを常にポップする必要はありません。
クイックサンプルプロジェクト
https://github.com/hanscappelle/so-2250770.gitで見つけることができます。プロジェクトのクローンを作成してAndroidStudioで開きます(gradle)。ListView
このプロジェクトには、すべてのランダムデータを使用して構築するMainAcitivityがあります。このリストは、アクションメニューを使用して更新できます。
この例のModelObject用に作成したアダプターの実装は、データ収集を公開します
public class MyListAdapter extends BaseAdapter {
/**
* this is our own collection of data, can be anything we
* want it to be as long as we get the abstract methods
* implemented using this data and work on this data
* (see getter) you should be fine
*/
private List<ModelObject> mData;
/**
* our ctor for this adapter, we'll accept all the things
* we need here
*
* @param mData
*/
public MyListAdapter(final Context context, final List<ModelObject> mData) {
this.mData = mData;
this.mContext = context;
}
public List<ModelObject> getData() {
return mData;
}
// implement all abstract methods here
}
MainActivityからのコード
public class MainActivity extends Activity {
private MyListAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView list = (ListView) findViewById(R.id.list);
// create some dummy data here
List<ModelObject> objects = getRandomData();
// and put it into an adapter for the list
mAdapter = new MyListAdapter(this, objects);
list.setAdapter(mAdapter);
// mAdapter is available in the helper methods below and the
// data will be updated based on action menu interactions
// you could also keep the reference to the android ListView
// object instead and use the {@link ListView#getAdapter()}
// method instead. However you would have to cast that adapter
// to your own instance every time
}
/**
* helper to show what happens when all data is new
*/
private void reloadAllData(){
// get new modified random data
List<ModelObject> objects = getRandomData();
// update data in our adapter
mAdapter.getData().clear();
mAdapter.getData().addAll(objects);
// fire the event
mAdapter.notifyDataSetChanged();
}
/**
* helper to show how only changing properties of data
* elements also works
*/
private void scrambleChecked(){
Random random = new Random();
// update data in our adapter, iterate all objects and
// resetting the checked option
for( ModelObject mo : mAdapter.getData()) {
mo.setChecked(random.nextBoolean());
}
// fire the event
mAdapter.notifyDataSetChanged();
}
}
詳しくは
listViewsの力に関するもう1つのすばらしい投稿は、次の場所にあります:http ://www.vogella.com/articles/AndroidListView/article.html
必要なときにいつでもrunnableを呼び出します。
runOnUiThread(run);
OnCreate()
、実行可能なスレッドを設定します。
run = new Runnable() {
public void run() {
//reload content
arraylist.clear();
arraylist.addAll(db.readAll());
adapter.notifyDataSetChanged();
listview.invalidateViews();
listview.refreshDrawableState();
}
};
リストビューの動的更新で問題が発生しました。
アダプタでnotifyDataSetChanged()を呼び出します。
notifyDataSetChanged()を呼び出す方法/タイミングに関するいくつかの追加の詳細は、このGoogle I/Oビデオで見ることができます。
私の場合、notifyDataSetChanged()が正しく機能しませんでした[別のクラスからnotifyDataSetChangedを呼び出しました]。念のため、実行中のアクティビティ(スレッド)でListViewを編集しました。クリストファーのおかげでそのビデオは最後のヒントを与えました。
私の2番目のクラスで私は使用しました
Runnable run = new Runnable(){
public void run(){
contactsActivity.update();
}
};
contactsActivity.runOnUiThread(run);
アクティビティからupdate()にアクセスします。このアップデートには以下が含まれます
myAdapter.notifyDataSetChanged();
アダプターにビューを更新するように指示します。私が言うことができる限りうまくいきました。
SimpleCursorAdapterを使用している場合は、Cursorオブジェクトでrequery()を呼び出してみてください。
それでもListViewの更新に満足できない場合は、このスニペットを見ることができます。これはDBからlistViewをロードするためのものです。実際には、CRUD操作を実行した後、ListViewをリロードするだけです。これは最善の方法ではありません。コードですが、必要に応じてListViewが更新されます。
それは私のために働きます....あなたがより良い解決策を見つけたら、共有してください...
.....。 .....。 CRUD操作を実行します。 .....。 ....。 DBAdapter.open(); DBAdapter.insert_into_SingleList(); //そのDB_resultsを持ってきて、その内容としてリストに追加します...。 ls2.setAdapter(new ArrayAdapter(DynTABSample.this、 android.R.layout.simple_list_item_1、DBAdapter.DB_ListView)); DBAdapter.close();
この投稿で人々によって提案された解決策は、主にデバイスのAndroidバージョンに応じて機能するかどうかに関係なく機能します。たとえば、AddAllメソッドを使用するには、Androidデバイスにandroid:minSdkVersion="10"を配置する必要があります。
すべてのデバイスについてこの質問を解決するために、アダプターで独自のメソッドを作成し、Addおよびremoveメソッド内で使用して、問題なくデータを更新するArrayAdapterから継承します。
私のコード:私自身のデータクラスRaceResultを使用して、あなたはあなた自身のデータモデルを使用します。
ResultGpRowAdapter.java
public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> {
Context context;
int resource;
List<RaceResult> data=null;
public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects) {
super(context, resource, objects);
this.context = context;
this.resource = resource;
this.data = objects;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
........
}
//my own method to populate data
public void myAddAll(List<RaceResult> items) {
for (RaceResult item:items){
super.add(item);
}
}
ResultsGp.java
public class ResultsGp extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...........
...........
ListView list = (ListView)findViewById(R.id.resultsGpList);
ResultGpRowAdapter adapter = new ResultGpRowAdapter(this, R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data
list.setAdapter(adapter);
....
....
....
//LOAD a ArrayList<RaceResult> with data
ArrayList<RaceResult> data = new ArrayList<RaceResult>();
data.add(new RaceResult(....));
data.add(new RaceResult(....));
.......
adapter.myAddAll(data); //Your list will be udpdated!!!
SQLデータベースの情報を変更した後、リストビュー(具体的には拡張可能なリストビュー)を更新することはできませんでした。notifyDataSetChanged()が役に立たない場合は、最初にリストをクリアしてから、notifyDataSetChanged()を呼び出してから再度追加してください。例えば
private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();
それがあなたにとって理にかなっていることを願っています。
更新時にスクロール位置を維持したい場合は、次のようにします。
if (mEventListView.getAdapter() == null) {
EventLogAdapter eventLogAdapter = new EventLogAdapter(mContext, events);
mEventListView.setAdapter(eventLogAdapter);
} else {
((EventLogAdapter)mEventListView.getAdapter()).refill(events);
}
public void refill(List<EventLog> events) {
mEvents.clear();
mEvents.addAll(events);
notifyDataSetChanged();
}
詳細については、Android ListView:更新時にスクロール位置を維持するを参照してください。
あなたはあなたが膨らませているデータを持っているそのリストの単一のオブジェクトを使用する必要がありますListView
。参照が変更された場合は notifyDataSetChanged()
機能しません。リストビューから要素を削除するときはいつでも、それがArrayList <>であるか他の何かであるかにかかわらず、使用しているリストから要素を削除し、
notifyDataSetChanged()
アダプタクラスのオブジェクトを呼び出します。
だからここで私が私のアダプターでそれをどのように管理したかを見てください以下を見てください
public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{
private Context context;
private ArrayList<CountryDataObject> dObj;
private ViewHolder holder;
private Typeface itemFont;
private int selectedPosition=-1;
private ArrayList<CountryDataObject> completeList;
public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) {
this.context = context;
this.dObj=dObj;
completeList=new ArrayList<CountryDataObject>();
completeList.addAll(dObj);
itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf");
}
@Override
public int getCount() {
return dObj.size();
}
@Override
public Object getItem(int position) {
return dObj.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
if(view==null){
holder = new ViewHolder();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.states_inflator_layout, null);
holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator));
holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState);
view.setTag(holder);
}else{
holder = (ViewHolder) view.getTag();
}
holder.textView.setText(dObj.get(position).getCountryName());
holder.textView.setTypeface(itemFont);
if(position==selectedPosition)
{
holder.checkImg.setImageResource(R.drawable.check);
}
else
{
holder.checkImg.setImageResource(R.drawable.uncheck);
}
return view;
}
private class ViewHolder{
private TextView textView;
private ImageView checkImg;
}
public void getFilter(String name) {
dObj.clear();
if(!name.equals("")){
for (CountryDataObject item : completeList) {
if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){
dObj.add(item);
}
}
}
else {
dObj.addAll(completeList);
}
selectedPosition=-1;
notifyDataSetChanged();
notifyDataSetInvalidated();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Registration reg=(Registration)context;
selectedPosition=position;
reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode());
notifyDataSetChanged();
}
}
myArrayList.remove(position);
リスナー内で使用するだけです。
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override public void onItemClick(AdapterView<?> parent, android.view.View view, int position, long id) {
myArrayList.remove(position);
myArrayAdapter.notifyDataSetChanged();
}
});
アダプターにリストを渡したと考えてください。
使用する:
list.getAdapter().notifyDataSetChanged()
リストを更新します。
リストビューからデータを削除した後、を呼び出す必要がありますrefreshDrawableState()
。次に例を示します。
final DatabaseHelper db = new DatabaseHelper (ActivityName.this);
db.open();
db.deleteContact(arg3);
mListView.refreshDrawableState();
db.close();
クラスのdeleteContact
メソッドは次のようになりますDatabaseHelper
public boolean deleteContact(long rowId) {
return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;
}
サービスからUIリストビューを更新する場合は、メインアクティビティでアダプターを静的にして、次のようにします。
@Override
public void onDestroy() {
if (MainActivity.isInFront == true) {
if (MainActivity.adapter != null) {
MainActivity.adapter.notifyDataSetChanged();
}
MainActivity.listView.setAdapter(MainActivity.adapter);
}
}
SimpleAdapterの更新でnotifyDataSetChanged()を機能させることができなかったため、代わりに、removeAllViews()を使用して親レイアウトにアタッチされているすべてのビューを削除し、次にListViewを追加しようとしました。これにより、 UI:
LinearLayout results = (LinearLayout)findViewById(R.id.results);
ListView lv = new ListView(this);
ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row,
new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } );
for (...) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", name);
map.put("dept", dept);
list.add(map);
}
lv.setAdapter(adapter);
results.removeAllViews();
results.addView(lv);
SimpleCursorAdapterを使用している間、アダプタでchangeCursor(newCursor)を呼び出すことができます。
フラグメントで、ListViewに(単一のTextViewで)しばらくの間スキャンされたBLEデバイスのMACアドレスを入力したいときも同じでした。
私がしたことはこれでした:
public class Fragment01 extends android.support.v4.app.Fragment implements ...
{
private ListView listView;
private ArrayAdapter<String> arrayAdapter_string;
...
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
...
this.listView= (ListView) super.getActivity().findViewById(R.id.fragment01_listView);
...
this.arrayAdapter_string= new ArrayAdapter<String>(super.getActivity(), R.layout.dispositivo_ble_item, R.id.fragment01_item_textView_titulo);
this.listView.setAdapter(this.arrayAdapter_string);
}
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
...
super.getActivity().runOnUiThread(new RefreshListView(device));
}
private class RefreshListView implements Runnable
{
private BluetoothDevice bluetoothDevice;
public RefreshListView(BluetoothDevice bluetoothDevice)
{
this.bluetoothDevice= bluetoothDevice;
}
@Override
public void run()
{
Fragment01.this.arrayAdapter_string.add(new String(bluetoothDevice.toString()));
Fragment01.this.arrayAdapter_string.notifyDataSetChanged();
}
}
次に、ListViewは、見つかったデバイスのMACアドレスを動的に入力し始めました。
リフレッシュとはどういう意味かによると思います。GUI表示を更新する必要があるということですか、それとも、プログラムでgetChildAt(int)を呼び出して、アダプター内にあるものに対応するビューを取得できるように、子ビューを更新する必要があるということですか。
GUI表示を更新する場合は、アダプターでnotifyDataSetChanged()を呼び出します。GUIは、次に再描画されたときに更新されます。
getChildAt(int)を呼び出して、アダプターの内容を反映するビューを取得できるようにする場合は、layoutChildren()を呼び出します。これにより、アダプタデータから子ビューが再構築されます。
リストビューに表示したいArrayListがありました。ArrayListにはmysqlの要素が含まれていました。onRefreshメソッドをオーバーライドし、そのメソッドでtablelayout.removeAllViews();を使用しました。次に、データベースからデータを再度取得するプロセスを繰り返しました。ただし、その前に、ArrayListまたはデータ構造をクリアしてください。そうしないと、古いデータに新しいデータが追加されます。
Androidのガイドラインに従っていて、からデータを取得するためにを使用していて、およびを使用しContentProviders
てデータをDatabase
表示している場合、関連データへのすべての変更は自動的にListViewに反映されます。ListView
CursorLoader
CursorAdapters
getContext().getContentResolver().notifyChange(uri, null);
のカーソル上でContentProvider
変更を反映するのに十分です。余分な回避策は必要ありません。
ただし、これらすべてを使用していない場合は、データセットがいつ変更されるかをアダプターに通知する必要があります。notifyDataSetChanged()
また、データセット(リストなど)を再入力/再ロードしてから、アダプターを呼び出す必要があります。
notifyDataSetChanged()
データセットに変更がない場合は機能しません。これがドキュメントのメソッドの上のコメントです-
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
新しいアダプターデータを取得し、リストビューのアダプターをリセットしてから、次のように呼び出すことによってのみ、notifyDataSetChangedを取得できました。
expandableAdapter = baseFragmentParent.setupEXLVAdapter();
baseFragmentParent.setAdapter(expandableAdapter);
expandableAdapter.notifyDataSetChanged();
他のオプションはonWindowFocusChanged
メソッドですが、その機密性が高く、興味のある人のために追加のコーディングが必要であることを確認してください
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
// some controls needed
programList = usersDBHelper.readProgram(model.title!!)
notesAdapter = DailyAdapter(this, programList)
notesAdapter.notifyDataSetChanged()
listview_act_daily.adapter = notesAdapter
}
ここで私のシナリオについて話した場合、削除ボタンとともにdb値のリストを表示するアクティビティがあり、削除ボタンが押されたときにそのアイテムをリストから削除したかったため、上記の回答以外は機能しません。
すばらしいのは、リサイクラービューではなく、単純なリストビューと、アダプタークラスで初期化されたリストビューを使用したことです。したがって、を呼び出すとnotifyDataSetChanged()
、アダプタクラス内、およびdeleteメソッドがアダプタクラスにあったためにアダプタオブジェクトが初期化されるアクティビティクラスでも何も実行されません。
したがって、解決策は、アダプタクラスgetView
メソッドでアダプタからオブジェクトを削除することでした(その特定のオブジェクトのみを削除しますが、すべてを削除する場合は、を呼び出しますclear()
)。
あなたがいくつかのアイデアを得るために、私のコードはどのように見えましたか、
public class WordAdapter extends ArrayAdapter<Word> {
Context context;
public WordAdapter(Activity context, ArrayList<Word> words) {}
//.......
@NonNull
@Override
public View getView(final int position, View convertView, ViewGroup group) {
//.......
ImageButton deleteBt = listItemView.findViewById(R.id.word_delete_bt);
deleteBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (vocabDb.deleteWord(currentWord.id)) {
//.....
} else{
//.....
}
remove(getItem(position)); // <---- here is the trick ---<
//clear() // if you want to clear everything
}
});
//....
注:ここremove()
とgetItem()
メソッドは、Adapterクラスから継承されます。
remove()
-クリックされた特定のアイテムを削除しますgetItem(position)
-クリックした位置からアイテム(ここでは、リストに追加したWordオブジェクト)を取得します。
これは、アクティビティクラスのリストビューにアダプタを設定する方法です。
ArrayList<Word> wordList = new ArrayList();
WordAdapter adapter = new WordAdapter(this, wordList);
ListView list_view = (ListView) findViewById(R.id.activity_view_words);
list_view.setAdapter(adapter);
最も簡単なのは、新しいAdaperを作成し、古いAdaperを削除することです。
myListView.setAdapter(new MyListAdapter(...));