关于ListView优化陋解

来源:互联网 发布:中国企业网络黄页 编辑:程序博客网 时间:2024/06/05 00:29

listView:是适配器视图,即可以建立新的布局(Item),通过setAdapter()方法,将布局添加给listView

          

      基于其原理,ListView的优化有四种方式:

                                一、内存优化(contertView):即复用已经显示并不在屏幕上的item

                                二、时间优化(ViewHolder):即减少findViewById()的次数

                                三、将ListView的宽和高设置为match_parent:即减少其布局的加载次数                              

                                四、分页加载:即通过OnscrollStateChange()方法和OnScroll()方法,设置监听。

一:内存优化

  首先让我们来看看listview比较简陋的写法吧。

public class MyBaseAdapter extends BaseAdapter{    private List<String> datas;    private Context context;    private LayoutInflater inflater;    public MyBaseAdapter(List<String> datas, Context context) {        this.datas = datas;        this.context = context;        inflater = LayoutInflater.from(context);    }    /**     * 获取数据源大小     * @return     */    @Override    public int getCount() {        return mDatas == null ? 0 : mDatas.size();    }    @Override    public Object getItem(int position) {        return null;    }    @Override    public long getItemId(int position) {        return 0;    }    /**     * 加载视图,数据     * @param position     * @param convertView     * @param parent     * @return     */    @Override    public View getView(int position, View convertView, ViewGroup parent) {        //布局就偷个懒,直接用系统自带的就好了        View view = LayoutInflater.from(MainActivity.this).inflate(android.R.layout.simple_list_item_1,parent,false);        TextView text = (TextView) view.findViewById(android.R.id.text1);        text.setText(datas.get(position));        return view;    }}

        使用的是BaseAdapter()来满足不同的需求,其中getCount()代表的是获取数据源的大小,如果有size = 5 ,那么getView()就会执行五次,其中position是从0~4,如果size = 0,那么getView将不会执行,getview是将你书写的xml布局以及添加的数据设置到adapter里面(每次getView()都会返回一个view(当然这个view可能是服复用的convertView)),然后通过adapter将带有数据的所有视图添加到listview里面。

 有些朋友可能在怀疑什么系统自带的?一脸懵逼,ok,我把源码粘贴出来,系统其实给我们提供了很多好东西,用好这些,可以加速你的App开发

?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2006 The Android Open Source Project     Licensed under the Apache License, Version 2.0 (the "License");     you may not use this file except in compliance with the License.     You may obtain a copy of the License at            http://www.apache.org/licenses/LICENSE-2.0       Unless required by applicable law or agreed to in writing, software     distributed under the License is distributed on an "AS IS" BASIS,     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.     See the License for the specific language governing permissions and     limitations under the License.--><TextView xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@android:id/text1"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:textAppearance="?android:attr/textAppearanceListItemSmall"    android:gravity="center_vertical"    android:paddingStart="?android:attr/listPreferredItemPaddingStart"    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"    android:minHeight="?android:attr/listPreferredItemHeightSmall" />
  什么也木有就是一个简单的textView,属性就不介绍了,ok,扯远了,回归。回归。

        BaseAdapter使用相对自由,可以在Item布局中放入图片(ImageView)、放入文字(TextView)、   放置按钮(Button)。。。按需设置。当然这按自己需求来,simpleAdapter,ArrayAdapter有时候也能很好用(当然他们也是BaseAdapter的子类),从getView()代码可以看出每添加一个TextView就会重新LaoutInflater.from.Inflate()一次,然后将item布局加载至adapter中,同时也会执行findViewById()通过id来找到textview,在数据量比较大的时  候,一次加载大量数据,不仅浪费用户流量,还会拖慢手机运行速度,甚至导致OOM,所以需要寻找一个办法,来f复用view,来服用找到的textview。

内存优化代码:

public class MyBaseAdapter extends BaseAdapter{    private List<String> datas;    private Context context;    private LayoutInflater inflater;    public MyBaseAdapter(List<String> datas, Context context) {        this.datas = datas;        this.context = context;        inflater = LayoutInflater.from(context);    }    /**     * 获取数据源大小     * @return     */    @Override    public int getCount() {        return mDatas == null ? 0 : mDatas.size();    }    @Override    public Object getItem(int position) {        return null;    }    @Override    public long getItemId(int position) {        return 0;    }    /**     * 加载视图,数据     * @param position     * @param convertView     * @param parent     * @return     */    @Override    public View getView(int position, View convertView, ViewGroup parent) {        View view = convertView;        if(null == view){        //布局就偷个懒,直接用系统自带的就好了         view = LayoutInflater.from(MainActivity.this).inflate(android.R.layout.simple_list_item_1,parent,false);        }        TextView text = (TextView) view.findViewById(android.R.id.text1);        text.setText(datas.get(position));        return view;    }}

你看见的没错就是这么简单,系统其实早已把ConvertVIew准备好了,那么什么是ConvertView尼?

当你划啊划啊,listview上边划出屏幕,那些item彻底看不见的时候,他们就是ConvertView,但是他们仍然是你通过意图过滤器得到的View哦。

二,时间优化:

public class MyBaseAdapter extends BaseAdapter{    private List<String> datas;    private Context context;    private LayoutInflater inflater;    public MyBaseAdapter(List<String> datas, Context context) {        this.datas = datas;        this.context = context;        inflater = LayoutInflater.from(context);    }    /**     * 获取数据源大小     * @return     */    @Override    public int getCount() {        return mDatas == null ? 0 : mDatas.size();    }    @Override    public Object getItem(int position) {        return null;    }    @Override    public long getItemId(int position) {        return 0;    }    /**     * 加载视图,数据     * @param position     * @param convertView     * @param parent     * @return     */    @Override    public View getView(int position, View convertView, ViewGroup parent) {        View view = convertView;        ViewHolder viewHolder;        if(null == view){        //布局就偷个懒,直接用系统自带的就好了            //当不存在服用的view时候就创建view并创建viewHolder            view = LayoutInflater.from(MainActivity.this).inflate(android.R.layout.simple_list_item_1,parent,false);            viewHolder = new ViewHolder(view);        }else {            //当又可以服用的view时候就去复用,并通过view来得到Holder(之前view用的时候就有)            viewHolder = (ViewHolder) view.getTag();        }        viewHolder.textView.setText(datas.get(position));        return view;    }}class ViewHolder{    TextView textView;    public ViewHolder(View view) {        view.setTag(this);        textView = (TextView) view.findViewById(android.R.id.text1);    }}
可能有些朋友要问什么时候服用的view有ViewHolder了。

请看我们在ViewHolder的构造方法里面传进了view,然后给这个view了一个Tag,至于setTag(),你可以把它想成这时候Viewholder就是View的一个属性,有setTag()。就有getTag(),他们是向对应的,是不是很牛逼,有get到新技能吧。如果要用多个setTag(),可以使用set Tag(key,value)形式,这时候系统要求key要唯一,我们可以到res的values的String里面给他命名一个id。

<resources>    <string name="app_name">march_day16_listview_paging</string>    <item type="id" name="tag_one"/>    <item type="id" name="tag_two"/></resources>
然后这时候的代码我们可以这样玩儿

 /**     * 加载视图,数据     * @param position     * @param convertView     * @param parent     * @return     */    @Override    public View getView(int position, View convertView, ViewGroup parent) {        View view = convertView;        ViewHolder viewHolder;        if(null == view){        //布局就偷个懒,直接用系统自带的就好了            //当不存在服用的view时候就创建view并创建viewHolder            view = LayoutInflater.from(MainActivity.this).inflate(android.R.layout.simple_list_item_1,parent,false);            viewHolder = new ViewHolder(view);        }else {            //当又可以服用的view时候就去复用,并通过view来得到Holder(之前view用的时候就有)            viewHolder = (ViewHolder) view.getTag(R.id.tag_one);        }        String tag = (String) view.getTag(R.id.tag_two);        viewHolder.textView.setText(datas.get(position)+tag);        return view;    }}class ViewHolder{    TextView textView;    public ViewHolder(View view) {        view.setTag(this);        view.setTag(R.id.tag_one,view);        view.setTag(R.id.tag_two,"大灰狼的故事");        textView = (TextView) view.findViewById(android.R.id.text1);    }}
有木有很神奇,至于领悟到什么就看你自己咯。

以上代码实现了优化方式一和方式二,其中的concertView是屏幕范围内已经不可见的item的布局,通过它来复用这些已经存在的布局,来达到内存的优化(不再创建新的Item)同时又通过ViewHolder来 使得这些回收的布局,不用再次findViewById(),来达到时间的优化(减少find次数,运行时间优化); 
第三种方式是,减少ListView的测量次数,即 在xml文件中将其属性设置为match_parent ,或者给一个确切的高度如200dp
  
第四种方式是:给其设置分页,即一次加载适量的Item,即通过OnScrollStateChange()和OnScroll()方法  
来给item的数量设置监听,在其向下滑动的时候,如果数据量达到某值,就会加载新的数据。  

  

  
马上要锁门了,读者见谅,下集将介绍listView来实现分类,再补上)  


一家之言,有误望君扶正,多谢。


2 0
原创粉丝点击