ListView 局部刷新

来源:互联网 发布:mac 下载ipython 编辑:程序博客网 时间:2024/05/18 02:45

概述

在使用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;    }    @Override    public int getCount() {        if(datas != null){            return datas.size();        }        return 0;    }    @Override    public Integer getItem(int position) {        return datas.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public 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;    @Override    protected 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() {            @Override            public 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() {     @Override     public 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;    }    @Override    public int getCount() {        if(datas != null){            return datas.size();        }        return 0;    }    @Override    public MyModule getItem(int position) {        return datas.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public 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>();    @Override    protected 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() {            @Override            public 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)

0 1
原创粉丝点击