ListView加载性能优化---ViewHolder---分页

    ListView是Android中一个重要的组件,可以使用它加列表数据,用户可以自己定义列表数据,同时ListView的数据加载要借助Adapter,一般情况下要在Adapter类中重写getCount(),getItem(),getItemId(),getView()四个方法。自定义列表项,以及数据的加载在getView()中处理。当ListView加载的列表项数据过多时,会占用大量的内存,影响性能,因此要对ListView进行优化。

     getView的加载方法有3种形式,如下:

     1.每次创建一个view,然后加载数据,加载速度慢

        /**
         * 1.每次都创建一个View
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater = LayoutInflater.from(context);
            View view = inflater.inflate(R.layout.listview_item_two,null);
            ImageView iv = (ImageView) view.findViewById(R.id.lv_listview_adapter_img);
            TextView tv = (TextView) view.findViewById(R.id.tv_listview_adapter_name);
            iv.setImageResource(icons[position]);
            tv.setText(titles[position]);
            return view;
        }

  2.当convertView不为空的时候直接重新使用convertView,为空时新建view,这样可以减少了很多不必要的View的创建,然后加载数据

       /**
         * 2.复用convertView,减少不必要的创建
         * 当convertView不为空的时候直接重新使用convertView从而减少了很多不必要的View的创建,然后加载数据
         * @param position
         * @param convertView
         * @param parent
         * @return
         */
       @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if(convertView==null){
                LayoutInflater inflater = LayoutInflater.from(context);
                convertView = inflater.inflate(R.layout.listview_item_two,null);
            }
            ImageView iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img);
            TextView tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name);
            iv.setImageResource(icons[position]);
            tv.setText(titles[position]);
            return convertView;
        }

  3.定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可

  ViewHolder将需要缓存的view封装好,convertView的setTag才是将这些缓存起来供下次调用。 当你的listview里布局多样化的时候 viewholder的作用体现明显,效率再一次提高。 View的findViewById()方法也是比较耗时的,因此需要考虑只调用一次,之后就用View.getTag()方法来获得ViewHolder对象。

当加载100条数据时,采用分页,每页加载20条,相当于创建了20个convertview。再加载21-40这二十条时,不需要重新创建20个convertview。第21条复用第1条的convertView,第22条复用第2条的convertView.......  

  @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder vh;
            if(convertView==null){
                LayoutInflater inflater = LayoutInflater.from(context);
                convertView = inflater.inflate(R.layout.listview_item_two,null);
                vh = new ViewHolder();
                vh.iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img);
                vh.tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name);
                convertView.setTag(vh);
            }else{
                vh = (ViewHolder)convertView.getTag();
            }
            vh.iv.setImageResource(icons[position]);
            vh.tv.setText(titles[position]);
            return convertView;
        }
        static class ViewHolder{
            ImageView iv;
            TextView tv;
        }

  以下是一个亲测的Demo案例,代码及运行结果如下:

     1.ListViewActivity.class 

public class ListViewActivity extends AppCompatActivity {

    private ListView listView_five;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view_adapter);

        listView_five = (ListView) findViewById(R.id.lv_listview_adater);

        listView_five.setAdapter(new MyAdapter(this));
        listView_five.setOnItemClickListener(new AdapterView.OnItemClickListener(){
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(ListViewActivity.this,"position="+position,Toast.LENGTH_LONG).show();
            }
        });
    }

    static class MyAdapter extends BaseAdapter{
        private String[] titles = {"title-1","title-2","title-3","title-4"};
        private int[] icons = {android.R.drawable.ic_lock_idle_alarm,android.R.drawable.ic_lock_idle_alarm,
                android.R.drawable.ic_lock_idle_alarm,android.R.drawable.ic_lock_idle_alarm};
        private Context context;
        public MyAdapter(Context context){
            this.context = context;
        }

        @Override
        public int getCount() {
            return titles.length;
        }

        @Override
        public Object getItem(int position) {
            return titles[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        /**
         * 1.每次都创建一个View
         */
        /*@Override
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater = LayoutInflater.from(context);
            View view = inflater.inflate(R.layout.listview_item_two,null);
            ImageView iv = (ImageView) view.findViewById(R.id.lv_listview_adapter_img);
            TextView tv = (TextView) view.findViewById(R.id.tv_listview_adapter_name);
            iv.setImageResource(icons[position]);
            tv.setText(titles[position]);
            return view;
        }*/

        /**
         * 2.复用convertView,减少不必要的创建
         * 当convertView不为空的时候直接重新使用convertView从而减少了很多不必要的View的创建,然后加载数据
         * @param position
         * @param convertView
         * @param parent
         * @return
         */
       /* @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if(convertView==null){
                LayoutInflater inflater = LayoutInflater.from(context);
                convertView = inflater.inflate(R.layout.listview_item_two,null);
            }
            ImageView iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img);
            TextView tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name);
            iv.setImageResource(icons[position]);
            tv.setText(titles[position]);
            return convertView;
        }*/

        /**
         * 3.定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可
         * @param position
         * @param convertView
         * @param parent
         * @return
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder vh;
            if(convertView==null){
                LayoutInflater inflater = LayoutInflater.from(context);
                convertView = inflater.inflate(R.layout.listview_item_two,null);
                vh = new ViewHolder();
                vh.iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img);
                vh.tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name);
                convertView.setTag(vh);
            }else{
                vh = (ViewHolder)convertView.getTag();
            }
            vh.iv.setImageResource(icons[position]);
            vh.tv.setText(titles[position]);
            return convertView;
        }
        static class ViewHolder{
            ImageView iv;
            TextView tv;
        }
    }
}

  

2.activity_list_view_adapter.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:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/lv_listview_adater"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:dividerHeight="10dp"
        android:listSelector="#00ff00"
        android:scrollbars="vertical|horizontal"
        />
</RelativeLayout>

  

3.listview_item_two.xml

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

    <ImageView
        android:id="@+id/lv_listview_adapter_img"
        android:src="@mipmap/gradview_dog_one"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/tv_listview_adapter_name"
        android:text="拉布拉多"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

三种方法的运行结果均相同,如下所示: 

 

参考网址:http://blog.csdn.net/jacman/article/details/7087995

     http://blog.csdn.net/pkxiuluo01/article/details/7380874

ListView分页的实现:

1.PageActivity.class

/**
 * ListView分页
 * 实现OnScrollListener监听
 */
public class PageActivity extends AppCompatActivity implements AbsListView.OnScrollListener{
    private ListView listView_six;
    private int index = 1;
    private MyAdapter myAdapter;
    private Vector<News> news = new Vector<>();//线程安全的容器
    private static final int DATA_UPDATE = 0x1;//数据更新完成后的标记
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_page);

        listView_six = (ListView) findViewById(R.id.lv_listview_page);
        listView_six.setOnScrollListener(this);
        //引入加载缓冲布局,并添加到ListView的底部
        View footView = getLayoutInflater().inflate(R.layout.load_item,null);
        listView_six.addFooterView(footView);
        initData();
        myAdapter = new MyAdapter(this);
        listView_six.setAdapter(myAdapter);
    }

    //加载数据,每次加载10条
    private void initData() {
        for(int i = 0;i < 10;i++){
            News n = new News();
            n.setTitle("title"+index);
            n.setContent("content"+index);
            index++;
            news.add(n);
        }
    }

    private int visibleLastIndex;//用来可显示的最后一条数据的索引
    /**
     *
     * @param view
     * @param scrollState 滚动状态
     * SCROLL_STATE_IDLE:不再滚动时(停止)
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(myAdapter.getCount() == visibleLastIndex && scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
            new loadDataThread().start();
        }

    }

    /**
     *
     * @param view
     * @param firstVisibleItem 第一次显示的
     * @param visibleItemCount 可显示的总量
     * @param totalItemCount
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        visibleLastIndex = firstVisibleItem + visibleItemCount -1;
    }

    //线程之间通讯机制
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case DATA_UPDATE:
                    //告知Adapter刷新数据
                    myAdapter.notifyDataSetChanged();
                    break;
                default:
                    break;
            }
        }
    };

    //子线程通知UI线程
    class loadDataThread extends Thread{
        @Override
        public void run() {
            super.run();
            //前10条数据加载完成时,再加载另外10条
            initData();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //通过Handler向主线程发送一个标记
            handler.sendEmptyMessage(DATA_UPDATE);
        }
    }
    //自定义Adapter
    class MyAdapter extends BaseAdapter{
        private Context context;
        public MyAdapter(Context context){
            this.context = context;
        }
        @Override
        public int getCount() {
            return news!=null?news.size():0;
        }

        @Override
        public Object getItem(int position) {
            return news.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder vh;
            if(convertView==null){
                LayoutInflater inflater = LayoutInflater.from(context);
                convertView = inflater.inflate(R.layout.listview_item_three,null);
                vh = new ViewHolder();
                vh.tv_one = (TextView) convertView.findViewById(R.id.tv_page_title);
                vh.tv_two = (TextView) convertView.findViewById(R.id.tv_page_content);
                convertView.setTag(vh);
            }else{
                vh = (ViewHolder)convertView.getTag();
            }

            News n = news.get(position);
            vh.tv_one.setText(n.getTitle()+"---");
            vh.tv_two.setText(n.getContent());
            return convertView;
        }
        class ViewHolder{
            TextView tv_one;
            TextView tv_two;
        }
    }
}

  2.activity_page.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.langdon.taiyang.androidtest.listview.PageActivity">

    <ListView
        android:id="@+id/lv_listview_page"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:dividerHeight="10dp"
        android:listSelector="#00ff00"
        android:scrollbars="vertical|horizontal"
        />
</LinearLayout>

  3.load_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.langdon.taiyang.androidtest.listview.PageActivity">

    <ListView
        android:id="@+id/lv_listview_page"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:dividerHeight="10dp"
        android:listSelector="#00ff00"
        android:scrollbars="vertical|horizontal"
        />
</LinearLayout>

  4.listview_item_three.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/tv_page_title"
        android:text="标题"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/tv_page_content"
        android:text="内容"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

  运行效果如下:

原文地址:https://www.cnblogs.com/langdon/p/6220993.html