ListView 局部刷新

来源:互联网转载 | 更新日期:2023-09-03 20:28:28

概述

在使用ListView的时候,我们都会对列表数据进行更新,当数据变化时,我们会调用adapter的notifyDataSetChange方法去刷新列表。但是,该刷新方法是使整个列表都更新一遍(调用了adapter的getView方法)。而,我们往往只是更新了item中的某一项数据,如果刷新整个列表是不是显得太过于浪费了,特别是对于列表中有图片要显示的情况下,就会造成每次notifyDataSetChange图片会闪烁抖动。本文意在解决该问题,让数据更新只在局部。

解决方案

  • 情景再现

下面例子的效果是,列表数据显示的是一系列数字,点击列表的item项时,该列表数字加1。那么一般的代码实现如下:
定义一个adapter

MyAdapter.java

public class MyAdapter extends BaseAdapter{private Context context;private List<Integer> datas;public MyAdapter(Context context,List<Integer> datas){this.context = context;this.datas = datas;}@Overridepublic int getCount() {if(datas != null){return datas.size();}return 0;}@Overridepublic Integer getItem(int position) {return datas.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView == null){convertView = LayoutInflater.from(context).inflate(R.layout.item_adapter,parent,false);holder = new ViewHolder();holder.textView = (TextView) convertView.findViewById(R.id.textview);convertView.setTag(holder);}else {holder = (ViewHolder) convertView.getTag();}holder.textView.setText(String.valueOf(getItem(position)));return convertView;}private class ViewHolder{TextView textView;} }

item 布局这里只是简单的一个TextView
item_adapter.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><TextView android:id="@+id/textview"android:layout_width="match_parent"android:layout_height="wrap_content"android:minHeight="45dp"android:textSize="18sp"android:gravity="center"android:textColor="@android:color/black"/> </LinearLayout>

activity的布局activity_listview.xlm

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><ListView android:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent"/> </LinearLayout>

数据显示

public class ListViewTest extends Activity{ListView mListView;MyAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_listview);mListView = (ListView) findViewById(R.id.listview);final List<Integer> datas = new ArrayList<Integer>();for (int i=1;i<10;i++){datas.add(i);}adapter = new MyAdapter(this,datas);mListView.setAdapter(adapter);mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {int data = datas.get(position);data = data+1;datas.set(position,data);adapter.notifyDataSetChanged();}});} }

这里主要是给ListView设置了setOnItemClickListener事件,然后进行+1处理,最后调用adapter的notifyDataSetChanged刷新数据。

好了,以上是我们一般的传统做法,下面我们看下如何实现ListView的局部刷新。

  • ListView局部刷新实现

在adapter里定义一个方法notifyDataSetChangedAt(View view,int position),然后在该方法处理逻辑
例如本例子如下:

public void notifyDataSetChangedAt(View view,int position) {if(view != null){int data = datas.get(position);data = data+1;datas.set(position,data);TextView textView = (TextView) view.findViewById(R.id.textview);textView.setText(String.valueOf(data));}}

然后在外部调用,实现调用,这里有两种场景,实现方法不同。

  • 第一种,在当前ListView点击item进行刷新
    这种场景是属于比较常规的,实现起来也是比较简单的,如下:
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {/*int data = datas.get(position);data = data+1;datas.set(position,data);adapter.notifyDataSetChanged();*/adapter.notifyDataSetChangedAt(view,position);} });

只要在ListView的setOnItemClickListener调用adapter的notifyDataSetChangedAt即可

  • 第二种,在‘详情页’进行操作后,需要刷新ListView

第二种比较复杂一点,和业务关系紧密,一般业务场景是这样的:比如微博,在微博详情页,我点赞了,那么微博的列表相应的item的赞数要加1,这种场景的复杂在于,业务操作点与ListView脱离,不知要更新哪个item,但是,一般我们的业务module都有自己的一个id,通过该id我们就可以计算出item的位置。

修改一下adapter的数据类型,增加module

public class MyModule {private int id;private int count;public int getId() {return id;}public void setId(int id) {this.id = id;}public int getCount() {return count;}public void setCount(int count) {this.count = count;} }

然后修改MyAdapter的数据类型为MyModule

public class MyAdapter extends BaseAdapter{private Context context;private List<MyModule> datas;public MyAdapter(Context context,List<MyModule> datas){this.context = context;this.datas = datas;}@Overridepublic int getCount() {if(datas != null){return datas.size();}return 0;}@Overridepublic MyModule getItem(int position) {return datas.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView == null){convertView = LayoutInflater.from(context).inflate(R.layout.item_adapter,parent,false);holder = new ViewHolder();holder.textView = (TextView) convertView.findViewById(R.id.textview);convertView.setTag(holder);}else {holder = (ViewHolder) convertView.getTag();}holder.textView.setText(String.valueOf(getItem(position).getCount()));return convertView;}private class ViewHolder{TextView textView;}/*** 局部刷新*/public void notifyDataSetChangedAt(View view,int position) {if(view != null){MyModule data = datas.get(position);data.setCount(data.getCount()+1);datas.set(position,data);TextView textView = (TextView) view.findViewById(R.id.textview);textView.setText(String.valueOf(data.getCount()));}} }

修改activity

public class ListViewTest extends Activity{ListView mListView;MyAdapter adapter;List<MyModule> datas = new ArrayList<MyModule>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_listview);mListView = (ListView) findViewById(R.id.listview);for (int i=0;i<10;i++){MyModule module = new MyModule();module.setId(i);module.setCount(i+1);datas.add(module);}adapter = new MyAdapter(this,datas);mListView.setAdapter(adapter);mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {/*int data = datas.get(position);data = data+1;datas.set(position,data);adapter.notifyDataSetChanged();*///adapter.notifyDataSetChangedAt(view,position);onListViewItemClick(datas.get(position));}});}private void onListViewItemClick(MyModule myModule){//获取屏幕中ListView可见部分的第一个item位置索引final int firstPos = mListView.getFirstVisiblePosition();//循环遍历for (int i = 0; i < datas.size(); i++) {MyModule module = datas.get(i);if (myModule != null && myModule.getId() == module.getId()) {View v = mListView.getChildAt(i - firstPos);adapter.notifyDataSetChangedAt(v, i);break;}}} }

我这里没有‘详情页’,通过ListView的ItemClickListener事件模拟了。最重要的方法是onListViewItemClick。思路:一般我们点击item进详情时,该item是处于在屏幕可见的,所以为了减少遍历次数,我们计算了ListView的第一个可见item的位置,然后通过比较是否是同一个module id实现局部刷新。

注意:一般使用场景是,详情页进行操作并更新module之后,通过setResult将module传给onActivityResult,然后调用onListViewItemClick(MyModule myModule)

上一篇:SecureCRT - 常用命令【自整理】

下一篇:LoRa无线通信技术介绍(三)数据包结构

相关文章

Copyright © 网站出售-网站交易平台 版权信息

网站备案号:黔ICP备2023004141号