如何刷新一个Android列表视图后添加/删除动态数据?
当前回答
本文中提出的解决方案是否有效主要取决于你的Android设备版本。例如,使用AddAll方法,你必须把android:minSdkVersion="10"在你的android设备。
为了解决所有设备的这个问题,我在适配器中创建了自己的方法,并在添加和删除方法中使用,该方法继承自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!!!
其他回答
请忽略所有的invalidate(), invalidateViews(), requestLayout(),…这个问题的答案。
正确的做法(幸运的是也被标记为正确答案)是在适配器上调用notifyDataSetChanged()。
故障排除
如果调用notifyDataSetChanged()不起作用,那么所有的布局方法也不会起作用。相信我,ListView已经正确更新了。如果您未能找到差异,则需要检查适配器中的数据来自何处。
如果这只是一个保存在内存中的集合,那么在调用notifyDataSetChanged()之前,请检查是否确实从集合中删除了项或向集合中添加了项。
如果使用的是数据库或服务后端,则必须在调用notifyDataSetChanged()之前调用该方法来再次检索信息(或操作内存中的数据)。
问题是这个notifyDataSetChanged只在数据集发生变化时有效。所以如果你没有发现变化,那就是你要看的地方。必要时进行调试。
ArrayAdapter vs BaseAdapter
我确实发现使用让您管理集合的适配器(如BaseAdapter)工作得更好。一些适配器(如ArrayAdapter)已经管理了自己的集合,因此很难获得适当的集合进行更新。在大多数情况下,这只是一个不必要的额外难度。
哎哟几天前
的确,这个必须从UI线程调用。其他答案有如何实现这一目标的例子。然而,只有当你在UI线程之外处理这些信息时,才需要这样做。它来自服务或非UI线程。在简单的情况下,您将通过单击按钮或其他活动/片段更新数据。仍然在UI线程中。不需要总是弹出runOnUiTrhead。
快速示例项目
可以在https://github.com/hanscappelle/so-2250770.git上找到。只需克隆并在Android Studio (gradle)中打开项目。这个项目有一个mainactitivity构建一个包含所有随机数据的ListView。可以使用操作菜单刷新此列表。
我为这个示例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功能的好文章可以在这里找到:http://www.vogella.com/articles/AndroidListView/article.html
在“dataArray”中添加/删除动态数据后:
如果你使用ArrayAdapter
adapter.notifyDataSetChanged();
如果你使用了扩展ArrayAdapter的customAdapter
adapter.clear();
adapter.addAll(dataArray);
adapter.notifyDataSetChanged();
如果你使用一个扩展BaseAdapter的customAdapter
adapter.clear();
adapter.getData().addAll(dataArray);
adapter.getData().notifyDataSetChanged();
一旦修改了适配器中的数据,就可以在适配器对象上调用notifyDataSetChanged()。
关于如何/何时调用notifyDataSetChanged()的其他细节可以在这个谷歌I/O视频中查看。
我也是一样,在一个片段中,我想用一段时间内扫描的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开始动态地填充找到的设备的mac地址。
您需要使用该列表的单个对象,您要在ListView中扩展该对象的数据。如果reference是change,那么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();
}
}
推荐文章
- 在应用程序本身中更改Locale
- apk (.apk)和应用程序包(.aab)的区别
- 如何设置超时在改造库?
- Android - SPAN_EXCLUSIVE_EXCLUSIVE跨度不能为零长度
- TextView的字体大小在Android应用程序改变字体大小从本机设置
- 如何模拟Android杀死我的进程
- 禁用EditText闪烁光标
- Android Eclipse -无法找到*.apk
- 设置TextView文本从html格式的字符串资源在XML
- 如何允许所有网络连接类型HTTP和HTTPS在Android(9)馅饼?
- Android加载JS包失败
- Android Studio, logcat在应用程序关闭后清理
- 在android中从上下文获取活动
- 无法解析主机"<URL here>"没有与主机名关联的地址
- getActivity()在Fragment函数中返回null