Android ListView优化之局部刷新(更新)(非notifyDataSetChanged)

   2016-11-04 0
核心提示:在Android开发中我们经常会用到listview的数据和界面刷新动作,我们每次可能会用到的都是Adapter.notifyDataSetChanged()方法。这个方法的原理是利用观察者模式对我们的数据源进行监听,当我们的数据源发生变化的时候,会调用Adapter的getView()方法进行整个

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

有的人可能会说,没有必要去优化这个。怎么说呢,至少这样会让我们更深入的去了解listview的特性。

先看效果图

先看一般的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"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <TextView
        android:text="万能适配器测试"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:gravity="center"
        android:textSize="18sp"
        android:textColor="#000000"
        android:id="@+id/textView" />
    </LinearLayout>
  • 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<String> 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<String>(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()方法会调用多次的情况:

Android ListView优化之局部刷新(更新)(非notifyDataSetChanged)

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<String> 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<String>(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));
        }
    }
}

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<String> 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<String>(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时调用了多次,在点击更新界面的时候只调用了一次。完美解决。

Android ListView优化之局部刷新(更新)(非notifyDataSetChanged)

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

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<T> datas;
    private int layoutId;

    public CommonAdapter(Context context, List<T> 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即可

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

 
标签: ListView
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • Android 自定义ListView adapter(zt)
    Android 自定义ListView adapter(zt)
    本文讲实现一个自定义列表的Android程序,程序将实现一个使用自定义的适配器(Adapter)绑定数据,通过contextView.setTag绑定数据有按钮的ListView。系统显示列表(ListView)时,首先会实例化一个适配器,本文将实例化一个自定义的适配器。实现自定义适配器
    12-01 ListView
  • Android ListView 优化之 getView 与 ViewHolder 是如何工作的?
    Android ListView 优化之 getView 与 ViewHolde
    Android中我们经常会用到ListView,然后ListView到底是如何通过ViewHolder去优化的?1.常见的适配器中利用ViewHolder去优化ListView的代码@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder;if (conve
    11-07 ListView
  • listviewd优化---viewHolder的封装
    listviewd优化---viewHolder的封装
    android项目中如果使用listview控件,则在优化上我们一般使用viewHolder保证列表项的布局convertView可以被重用避免多次重新绘制。 一般方法中getView的写法@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder vie
    11-07 安卓开发
  • 基础篇章:关于 React Native 之 ListView 组件的讲解
    基础篇章:关于 React Native 之 ListView 组件
    我们讲完ScrollView组件,其实顺其自然的就应该讲解ListView,对于前段和移动端的开发人员应该非常熟悉这样的控件吧,具体是做什么的,我感觉不用我讲了吧。我们来看看它怎么使用吧。大家好,我是ListView,我是React Native大家族中基础组件中,一个核心组件
  • Android ListView 使用不同对象加载不同布局
    Android ListView 使用不同对象加载不同布局
    因为未知原因,突然想到了关于一个 List 集合里面能否添加不同对象的问题,因为我们平时开发过程中,关于List 的比较常规的写法就是:ListXXX list = new ArrayListXXX();这让我形成了一种 List 里面就只能添加一种类型的对象的潜在想法(或许是 Java 基础不
    10-31 ListView
  • Android应用性能优化系列视图篇——ListView自适应导致的严重性能问题
    Android应用性能优化系列视图篇——ListView自
    ListView是Android中最常用的视图之一,使用的频率仅仅次于三大基础布局,虽然由于使用性和扩展性等原因备受争议,且尽管后来出现了RecyclerView的替代方案,但是ListView仍然广泛地使用在我们的项目中。自从ListView出道至今,已经不知道衍生出了多少问题,
  • android中listview的一些样式设置
    在 Android中,ListView是最常用的一个控件,在做UI设计的时候,很多人希望能够改变一下它的背景,使他能够符合整体的UI设计,改变背景背很简单只需要准备一张图片然后指定属性 android:background=”@drawable/bg”,不过不要高兴地太早,当你这么做以后,发
    10-13 ListView
  • React Native填坑之旅--ListView篇
    列表显示数据,基本什么应用都是必须。笔者写作的时候RN版本是0.34。今天就来从浅到深的看看React Native的ListView怎么使用。首先是使用写死的数据,之后会使用网络请求的数据在界面中显示。最后加上一个ActivityIndicator,网络请求的过程中显示Loading图标
  • RecyclerView、ListView 实现单选列表的优雅之路.
    RecyclerView、ListView 实现单选列表的优雅之
    一 概述: 这篇文章需求来源还是比较简单的,但做的 优雅 仍有值得挖掘的地方。 需求来源:一个类似饿了么这种 电商优惠券的选择界面 : 其实就是 一个普通的列表,实现了 单选 功能,效果如图:(不要怪图渣了,我撸了四五遍,公司录出来的GIF就这么渣。。。
  • [原]判断Listview滑动到了最底部(且最后一个ite
    记录下代码:listView.setOnScrollListener(new AbsListView.OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleIte
    09-14 ListView
点击排行