转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53020164 【DylanAndroid的csdn博客】


在Android开发中我们经常会用到listview的数据和界面刷新动作,我们每次可能会用到的都是Adapter.notifyDataSetChanged()方法。这个方法的原理是利用观察者模式对我们的数据源进行监听,当我们的数据源发生变化的时候,会调用Adapter的getView()方法进行整个界面的刷新。这样的话我们发现,getview()会调用多次,刷新了好多个不需要刷新的item,这样的话相对而言,降低了效率。但是,我们有的情况下是只需要对某个item的数据进行刷新就可以了。这样的话,当数据很多的时候,会提高效率。
有的人可能会说,没有必要去优化这个。怎么说呢,至少这样会让我们更深入的去了解listview的特性。

1.先看效果图

2.先看一般的Adapter.notifyDataSetChanged()方法刷新界面

  • 1.主界面的布局文件activity_main.xml

    <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#FFFFFF"tools:context="cn.bluemobi.dylan.listviewupdate.MainActivity"><ListView    android:id="@+id/listview"    android:divider="#666666"    android:dividerHeight="1px"    android:layout_width="wrap_content"    android:layout_height="wrap_content" />RelativeLayout>
  • 2.listview中的item的布局文件item.xml

    <?xml version="1.0" encoding="utf-8"?>"http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent">"万能适配器测试"    android:layout_width="match_parent"    android:layout_height="48dp"    android:gravity="center"    android:textSize="18sp"    android:textColor="#000000"    android:id="@+id/textView" />
  • 3.activity中的代码
    这里面用到了万能ViewHolder,不了解可以去这里http://blog.csdn.net/linglongxin24/article/details/52808656 了解详情
    同时也用到了万能适配器,不了解可以去这里http://blog.csdn.net/linglongxin24/article/details/52813227 了解详情
package cn.bluemobi.dylan.listviewupdate;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.AdapterView;import android.widget.ListView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import cn.bluemobi.dylan.listviewupdate.adapter.CommonAdapter;import cn.bluemobi.dylan.listviewupdate.adapter.CommonViewHolder;public class MainActivity extends AppCompatActivity {    private ListView listView;    private List datas;    private CommonAdapter commonAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        updateTest();    }    /**     * 一般的更新界面     */    private void updateTest() {        setContentView(R.layout.activity_main);        listView = (ListView) findViewById(R.id.listview);        datas = new ArrayList<>();        for (int i = 0; i < 10; i++) {            datas.add("万能适配器测试" + i);        }        final CommonAdapter commonAdapter = new CommonAdapter(this, datas, R.layout.item) {            @Override            protected void convertView(View item, String s) {                TextView textView = CommonViewHolder.get(item, R.id.textView);                textView.setText(s);            }        };        listView.setAdapter(commonAdapter);        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                datas.set(position, "update 万能适配器测试" + position);                commonAdapter.notifyDataSetChanged();            }        });    }    }

以上代码是较为常见的代码,我们在点击的时候将当前点击的item中的内容改变,我们会发现getView()方法会调用多次的情况:

3.ListView局部刷新方法一:更新对应view的内容

这种方法先通过listView.getChildAt(position)拿到要更新的对应的item布局文件,然后再通过findViewById找到对应的控件进行设置。

package cn.bluemobi.dylan.listviewupdate;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.AdapterView;import android.widget.ListView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import cn.bluemobi.dylan.listviewupdate.adapter.CommonAdapter;import cn.bluemobi.dylan.listviewupdate.adapter.CommonViewHolder;public class MainActivity extends AppCompatActivity {    private ListView listView;    private List datas;    private CommonAdapter commonAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        updateOneTest();    }    /**     * 只是局部更新某个界面     */    private void updateOneTest() {        setContentView(R.layout.activity_main);        listView = (ListView) findViewById(R.id.listview);        datas = new ArrayList<>();        for (int i = 0; i < 20; i++) {            datas.add("万能适配器测试" + i);        }        commonAdapter = new CommonAdapter(this, datas, R.layout.item) {            @Override            protected void convertView(View item, String s) {                TextView textView = CommonViewHolder.get(item, R.id.textView);                textView.setText(s);            }        };        listView.setAdapter(commonAdapter);        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                datas.set(position, "update 万能适配器测试" + position);                updateSingle(position);            }        });    }      /**     * 第一种方法 更新对应view的内容     *     * @param position 要更新的位置     */    private void updateSingle(int position) {        /**第一个可见的位置**/        int firstVisiblePosition = listView.getFirstVisiblePosition();        /**最后一个可见的位置**/        int lastVisiblePosition = listView.getLastVisiblePosition();        /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/        if (position >= firstVisiblePosition && position <= lastVisiblePosition) {            /**获取指定位置view对象**/            View view = listView.getChildAt(position - firstVisiblePosition);            TextView textView = (TextView) view.findViewById(R.id.textView);            textView.setText(datas.get(position));        }    }}

4.ListView局部刷新方法二:通过ViewHolder去设置值

通过Item找出对应的ViewHolder,然后通过ViewHolder去设置值

    /**     * 第二种方法 找出对应的ViewHolder,通过ViewHolder去设置值     *     * @param position 要更新的位置     */    private void updateOne(int position) {        /**第一个可见的位置**/        int firstVisiblePosition = listView.getFirstVisiblePosition();        /**最后一个可见的位置**/        int lastVisiblePosition = listView.getLastVisiblePosition();        /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/        if (position >= firstVisiblePosition && position <= lastVisiblePosition) {            /**获取指定位置view对象**/            View view = listView.getChildAt(position - firstVisiblePosition);            /**通过ViewHolder找出缓存的对应控件**/            TextView textView = CommonViewHolder.get(view, R.id.textView);            textView.setText(datas.get(position));        }    }

5.ListView局部刷新方法三:调用一次getView()方法

这种方法是调用适配器对应的getView方法,用它里面的代码对界面进行刷新。这也是google在IO大会上推荐的做法

package cn.bluemobi.dylan.listviewupdate;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.AdapterView;import android.widget.ListView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import cn.bluemobi.dylan.listviewupdate.adapter.CommonAdapter;import cn.bluemobi.dylan.listviewupdate.adapter.CommonViewHolder;public class MainActivity extends AppCompatActivity {    private ListView listView;    private List datas;    private CommonAdapter commonAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        updateOneTest();    }    /**     * 只是局部更新某个界面     */    private void updateOneTest() {        setContentView(R.layout.activity_main);        listView = (ListView) findViewById(R.id.listview);        datas = new ArrayList<>();        for (int i = 0; i < 20; i++) {            datas.add("万能适配器测试" + i);        }        commonAdapter = new CommonAdapter(this, datas, R.layout.item) {            @Override            protected void convertView(View item, String s) {                TextView textView = CommonViewHolder.get(item, R.id.textView);                textView.setText(s);            }        };        listView.setAdapter(commonAdapter);        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                datas.set(position, "update 万能适配器测试" + position);               updateItem(position);            }        });    }     /**     * 第三种方法 调用一次getView()方法;Google推荐的做法     *     * @param position 要更新的位置     */    private void updateItem(int position) {        /**第一个可见的位置**/        int firstVisiblePosition = listView.getFirstVisiblePosition();        /**最后一个可见的位置**/        int lastVisiblePosition = listView.getLastVisiblePosition();        /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/        if (position >= firstVisiblePosition && position <= lastVisiblePosition) {            /**获取指定位置view对象**/            View view = listView.getChildAt(position - firstVisiblePosition);            commonAdapter.getView(position, view, listView);        }    }    }

我们来看下日志:在初始化加载完listview时调用了多次,在点击更新界面的时候只调用了一次。完美解决。

6.最后封装在万能适配器当中

package cn.bluemobi.dylan.listviewupdate.adapter;import android.content.Context;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ListView;import java.util.List;/** * Created by yuandl on 2016-10-13. * 万能适配器 */public abstract class CommonAdapter<T> extends BaseAdapter {    private Context context;    private List datas;    private int layoutId;    public CommonAdapter(Context context, List datas, int layoutId) {        this.context = context;        this.datas = datas;        this.layoutId = layoutId;    }    @Override    public int getCount() {        return datas == null ? 0 : datas.size();    }    @Override    public T 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) {        if (convertView == null) {            convertView = LayoutInflater.from(context).inflate(layoutId, null);        }        Log.d("listview", "---------getView()-----------");        T t = getItem(position);        convertView(convertView, t);        return convertView;    }    /**     * 局部更新数据,调用一次getView()方法;Google推荐的做法     *     * @param listView 要更新的listview     * @param position 要更新的位置     */    public void notifyDataSetChanged(ListView listView, int position) {        /**第一个可见的位置**/        int firstVisiblePosition = listView.getFirstVisiblePosition();        /**最后一个可见的位置**/        int lastVisiblePosition = listView.getLastVisiblePosition();        /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/        if (position >= firstVisiblePosition && position <= lastVisiblePosition) {            /**获取指定位置view对象**/            View view = listView.getChildAt(position - firstVisiblePosition);            getView(position, view, listView);        }    }    /**     * 需要去实现的对item中的view的设置操作     *     * @param item     * @param t     */    protected abstract void convertView(View item, T t);}

这样的话,我们每次更新的时候只需要调用notifyDataSetChanged(ListView listView, int position),传入对应的要更新的listview和要更新的位置position即可

7.总结

这三种方法的核心就是找出你要更新Item的contentView.然后再去操作。因为ListView默认只会加载一屏的数据,所以要判断其可见范围。不可见的在滑动的时候getView会自动调用更新数据。最后要强调的一点就是关于布局优化,最好将item的高度设置为一个固定的值,这样能减少getView的调用次数。因为一个不确定的值,ListView会频繁调用多次getView去确定其高度和渲染。

8.GitHub源码地址:https://github.com/linglongxin24/ListViewUpdate

更多相关文章

  1. WindowManager和WindowManager.LayoutParams的使用以及实现悬浮
  2. Android(安卓)顶部灰条标题栏不显示的方法
  3. 彻底解决andorid h5交互!浅谈h5交互和js注入漏洞分析
  4. RecyclerView实现Item点击事件方法二
  5. android事件处理总结--dispatchTouchEvent【转】
  6. 属性动画(property animation) &重复执行
  7. Android(安卓)LayoutInflater.inflate()方法参数详解
  8. android的触摸机制
  9. Android短信彩信收发流程(应用层)

随机推荐

  1. android MediaPlayer SurfaceView 网络视
  2. Andriod studio编译问题:org.gradle.proce
  3. 简单的android Q 摄像头预览操作
  4. Android(安卓)实现手机震动功能
  5. Android自学笔记之短信发送器
  6. Android(安卓)使用decodeFile方法加载手
  7. android 代码中设置字体大小
  8. 打包错误:`Error:Execution failed for ta
  9. Android(安卓)获取通讯录联系人
  10. android 强制开启 GPS