如何刷新Android的ListView?
添加/删除dynamic数据后如何刷新Android ListView
?
一旦您修改了该适配器中的数据,就调用Adapter
对象上的notifyDataSetChanged()
。
关于如何/何时调用notifyDataSetChanged()
一些额外的细节可以在这个Google I / Ovideo中查看。
你也可以使用这个:
myListView.invalidateViews();
请享用!
请忽略所有invalidate()
, invalidateViews()
, requestLayout()
,…这个问题的答案。
正确的做法(幸运的是也被标记为正确的答案)是在适配器上调用notifyDataSetChanged()
。
故障排除
如果调用notifyDataSetChanged()
不起作用,所有的布局方法都不会有帮助。 相信我ListView
被正确更新。 如果您没有find差异,则需要检查适配器中的数据来自何处。
如果这只是您保存在内存中的一个集合,请在调用notifyDataSetChanged()
之前检查是否实际从项目中删除或添加了项目。
如果您正在使用数据库或服务后端,则必须在调用notifyDataSetChanged()
之前调用该方法来再次检索信息(或处理内存数据notifyDataSetChanged()
。
事情是这个notifyDataSetChanged
只适用于数据集已经改变。 所以如果你没有find任何改变,那么这个地方应该看。 如果需要debugging。
ArrayAdapter与BaseAdapter
我确实发现使用适配器来pipe理集合,就像BaseAdapter一样,效果更好。 一些像ArrayAdapter这样的适配器已经pipe理好了自己的集合,这使得难以获得适当的更新集合。 在大多数情况下,这只是一个不必要的额外困难。
UI线程
这是真的,这必须从UI线程调用。 其他答案有如何实现这个例子。 但是,只有在您从UI线程之外处理这些信息时才需要这样做。 这是从服务或非UI线程。 在简单的情况下,您将从点击button或其他活动/片段更新您的数据。 所以仍然在UI线程内。 不需要总是popuprunOnUiTrhead。
快速示例项目
可以在https://github.com/hanscappelle/so-2250770.gitfind。; 在Android Studio(gradle)中克隆并打开项目。 这个项目有一个MainAcitivity构build一个带有所有随机数据的ListView
。 该列表可以使用操作菜单进行刷新。
我为这个示例ModelObject创build的适配器实现公开了数据收集
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的力量另一个不错的职位是在这里find: 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(); } };
我有一些dynamic刷新我的列表视图的问题。
在适配器上调用notifyDataSetChanged()。
关于如何/何时调用notifyDataSetChanged()的一些额外的细节可以在这个Google I / Ovideo中查看。
notifyDataSetChanged()在我的情况下无法正常工作[我从另一个类中调用notifyDataSetChanged]。 就在这种情况下,我在运行Activity(Thread)中编辑ListView。 那个video感谢克里斯托弗给了最后的提示。
在我的第二堂课中,我使用了
Runnable run = new Runnable(){ public void run(){ contactsActivity.update(); } }; contactsActivity.runOnUiThread(run);
从我的Activity访问update()。 此更新包括
myAdapter.notifyDataSetChanged();
告诉Adapter刷新视图。 就我所能说的,工作得很好。
如果使用SimpleCursorAdapter,请尝试调用Cursor对象上的requery() 。
如果你不满意ListView Refreshment,你可以看看这个代码片段,这是从DB加载listView,实际上你要做的只是重新加载ListView,在你执行任何CRUD操作后,它不是一个最好的方法代码,但它会刷新ListView,如你所愿..
它适用于我….如果你find更好的解决scheme,请分享…
....... ...... 做你的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();
这篇文章中的人提出的解决scheme的工作或不主要取决于您的设备的Android版本。 例如,要使用AddAll方法,你必须把android:minSdkVersion =“10”放在你的android设备上。
为了解决所有设备的这个问题,我已经在适配器中创build了自己的方法,并且使用从ArrayAdapterinheritance的add和remove方法来更新数据,没有任何问题。
我的代码:使用我自己的数据类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!!!
如果在刷新时要保持滚动位置,可以这样做:
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:刷新时保持滚动位置 。
从列表视图中删除数据后,您必须调用refreshDrawableState()
。 这是一个例子:
final DatabaseHelper db = new DatabaseHelper (ActivityName.this); db.open(); db.deleteContact(arg3); mListView.refreshDrawableState(); db.close();
而DatabaseHelper
类中的deleteContact
方法会有点像
public boolean deleteContact(long rowId) { return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0; }
我无法得到notifyDataSetChanged()来更新我的SimpleAdapter,所以相反,我试着先删除所有使用removeAllViews()附加到父布局的视图,然后添加ListView,并且工作,允许我更新用户界面:
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);
对我来说,在更改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();
希望这对你有意义。
而使用SimpleCursorAdapter可以调用适配器上的changeCursor(newCursor)。
当我在一个片段中想要用一段时间扫描的BLE设备的MAC地址填充一个ListView(在一个TextView中)的时候,我是一样的。
我做的是这样的:
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开始dynamic填充所发现设备的mac地址。
我认为这取决于你的意思是刷新。 你的意思是应该刷新GUI显示,或者你的意思是应该刷新子视图,以便可以编程方式调用getChildAt(int)并获取与适配器中相应的视图。
如果要刷新GUI显示,请在适配器上调用notifyDataSetChanged()。 下次重新绘制时,GUI将被刷新。
如果您希望能够调用getChildAt(int)并获取反映适配器内容的视图,请调用layoutChildren()。 这将导致子视图从适配器数据重build。
我有一个ArrayList,我想在列表视图中显示。 ArrayList包含来自mysql的元素。 我重写了onRefresh方法,并在该方法中我使用tablelayout.removeAllViews(); 然后重复从数据库中获取数据的过程。 但在此之前,确保清除你的ArrayList或任何数据结构,否则新的数据将被附加到旧的..
如果你想从一个服务更新UI列表视图,那么在你的主要活动中使适配器变为静态,并执行以下操作:
@Override public void onDestroy() { if (MainActivity.isInFront == true) { if (MainActivity.adapter != null) { MainActivity.adapter.notifyDataSetChanged(); } MainActivity.listView.setAdapter(MainActivity.adapter); } }
你需要使用这个列表的单个对象whoose你在列表视图上膨胀的数据。如果refference是改变notifyDataSetChanged()不起作用。每当你从列表视图中删除元素也删除他们从你使用的清单是否它是一个ArrayList <>或其他东西然后调用适配器类的对象notifyDataSetChanged()。
所以这里看看我如何pipe理它在我的适配器见下文
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(); }
}
如果您正在使用android指南行,并且您正在使用ContentProviders
从Database
获取数据,并使用CursorLoader
和CursorAdapters
将其显示在ListView
,则所有对相关数据的更改将自动反映在ListView中。
你的getContext().getContentResolver().notifyChange(uri, null);
在ContentProvider
的游标将足以反映这些变化。无需额外的解决方法。
但是当你不使用这些,那么当数据集发生变化时,你需要告诉适配器。 你也需要重新填充/重新加载你的数据集(比如列表),然后你需要在适配器上调用notifyDataSetChanged()
。
如果数据集中没有更改, notifyDataSetChanged()
无法工作。 以下是docs中方法的注释 –
/** * 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();