1.getCount()方法:

  android提供了N多已经封装好的适配器,但用得最多还是BaseAdapter。如果写一个类继承BaseAdapter,则会看到它至少要覆写四个方法:

public class MAdapter extends BaseAdapter{@Overridepublic int getCount() {// TODO Auto-generated method stubreturn 0;}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn null;}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubreturn null;}}

  首先说一下getCount()方法,该方法返回一个int型的值,表示你的组件(ListView,GridView等)的长度,也就是要显示的item的数量。如果你的getCount()返回值是0的话,列表一行都不会显示,如果返回1,就只显示一行。返回几则显示几行。我们一般会向adapter传递我们的数据项list(比如mList),一般getCount()方法会这么写:

@Overridepublic int getCount() {// TODO Auto-generated method stubif (mList != null)return mList.size();else return 0;}

  但有时,比如你使用ListView,布局给它的位置能显示10个childitem,当传入的mList数量小于10,这个listView就只显示当前的子项数目而无法占满空间。有时为了美观,会让ListView至少显示10个(此时要自己处理,把多出来的子项显示为默认样式),所以也会这样写:

private static final int ITEM_COUNT = 10;@Overridepublic int getCount() {// TODO Auto-generated method stubif(mList != null && mList.size() > 10)return mList.size();else {return ITEM_COUNT;}}

2.getView方法(重点是convertView参数的缓存机制)

  那么以ListView为例,来说下系统如何绘制ListView(也就是如何加载,其他需要适配器的组件均类似)。ListView 针对每个item,要求 adapter “返回一个视图” (getView()方法),也就是说ListView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后根据这个长度,调用getView()一行一行的绘制ListView的每一项。于是,我们来看看getView()方法:

@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubreturn null;}

这个方法会返回一个View,显示出来就是ListView的一个子项(Child)。该方法有3个参数:

  position:从0开始,可以理解为adapter中数据集合的下标。通常情况下,这个position和你传入adapter的mList.get(position)中的position对应使用。

  convertView:这个convertView其实就是最关键的部分了。原理上讲,当ListView滑动的过程中 会有item被滑出屏幕 而不再被使用 这时候Android会回收这个条目的view,这个view也就是这里的convertView。

  其实到此为止我们可以总结出convertview的机制了,就是在初始显示的时候,每次显示一个item都调用一次getview方法但是每次调用的时候covertview为空(因为还没有旧的view),当显示完了之后。如果屏幕移动了之后,并且导致有些Item(也可以说是view)跑到屏幕外面,此时如果还有新的item需要产生,则这些item显示时调用的getview方法中的convertview参数就不是null,而是那些移出屏幕的view(旧view),我们所要做的就是将需要显示的item填充到这些回收的view(旧view)中去,最后注意convertview为null的不仅仅是初始显示的那些item,还有一些是已经开始移入屏幕但是还没有view被回收的那些item。

  上图中,当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个View。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新new一个View。

  最后一个parent参数暂时没用到过。

  不使用convertView的写法,每次都会new一个View,逻辑上没有任何问题,但是数据量很大的话,动不动就会OOM:

public View getView(int position, View convertView, ViewGroup parent) {View view = new View();
    //通过inflate等找到布局 然后findViewById等 设置各个显示的item
    return view;
}

  使用convertView,节省了new View的大量开销:

public View getView(int position, View convertView, ViewGroup parent) {View view = null;
    if (convertView != null) {view = convertView;
        //复用了回收的view 只需要直接作内容填充的修改就好了} else {view = new Xxx(...);
    //没有供复用的view 按一般的做法新建view
    }
    return view;
}

3.ViewHolder和getTag()、setTag()方法:

  先上一份使用ViewHolder自定义adapter的典型写法:

public class MarkerItemAdapter extends BaseAdapter
{private Context mContext = null;private List<MarkerItem> mMarkerData = null;public MarkerItemAdapter(Context context, List<MarkerItem> markerItems){mContext = context;mMarkerData = markerItems;}public void setMarkerData(List<MarkerItem> markerItems){mMarkerData = markerItems;}@Overridepublic int getCount(){int count = 0;if (null != mMarkerData){count = mMarkerData.size();}return count;}@Overridepublic MarkerItem getItem(int position){MarkerItem item = null;if (null != mMarkerData){item = mMarkerData.get(position);}return item;}@Overridepublic long getItemId(int position){return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent){ViewHolder viewHolder = null;if (null == convertView){viewHolder = new ViewHolder();LayoutInflater mInflater = LayoutInflater.from(mContext);convertView = mInflater.inflate(R.layout.item_marker_item, null);viewHolder.name = (TextView) convertView.findViewById(R.id.name);viewHolder.description = (TextView) convertView.findViewById(R.id.description);viewHolder.createTime = (TextView) convertView.findViewById(R.id.createTime);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}// set item values to the viewHolder:
MarkerItem markerItem = getItem(position);if (null != markerItem){viewHolder.name.setText(markerItem.getName());viewHolder.description.setText(markerItem.getDescription());viewHolder.createTime.setText(markerItem.getCreateDate());}return convertView;}private static class ViewHolder{TextView name;TextView description;TextView createTime;}}

  getTag()、setTag()方法很简单:View中的setTag(Object)表示给View添加一个额外的数据,以后可以用getTag()将这个数据取出来。

  可以看到ViewHolder就是一个内部类。当convertView为null时,老老实实加载xml布局文件,然后用findViewById把布局文件中的各个组件对应到ViewHolder的各个属性,并把ViewHolder用setTag()方法绑定到convertView。当convertView不为空可以复用时,直接使用getTag()方法取出绑定的ViewHolder,省去了findViewById()的开销。

4.getItemViewType(int position)和getViewTypeCount()方法

  但是上面的程序仍然有缺陷——当我们的ListView中填充的item有多种形式时,比如微博中,有的item中包含图片,有的item包含视频,那么必然的,我们需要用到2种item的布局方式。此时如果只是单纯判断convertView是否存在,会造成回收的view不符合你当前需要的布局,而类似转换失败出错退出。这里要提到Adapter中的另外2个方法:

  public int getItemViewType(int position) {}
  public int getViewTypeCount() {}

  从方法名上 就可以比较明显的明白这2个的作用
  下面附上一个demo代码:

class MyAdapter extends BaseAdapter{Context mContext;LinearLayout linearLayout = null;LayoutInflater inflater;TextView tex;
  final int VIEW_TYPE = 2;
  final int TYPE_1 = 0;
  final int TYPE_2 = 1;  public MyAdapter(Context context) {mContext = context;inflater = LayoutInflater.from(mContext);}@Override
  public int getCount() {
    return listString.size();}  //每个convert view都会调用此方法,获得当前所需要的view样式
  @Override
  public int getItemViewType(int position) {
    int p = position%6;
    if(p == 0)
      return TYPE_1;
    else if(p < 3)
      return TYPE_2;
    else
      return TYPE_1;}@Override
  public int getViewTypeCount() {
    return 2;}@Override
  public Object getItem(int arg0) {
    return listString.get(arg0);}@Override
    public long getItemId(int position) {
    return position;}@Override
  public View getView(int position, View convertView, ViewGroup parent) {viewHolder1 holder1 = null;viewHolder2 holder2 = null;
    int type = getItemViewType(position);    //无convertView,需要new出各个控件
    if(convertView == null){
    //按当前所需的样式,确定new的布局
      switch(type){
      case TYPE_1:convertView = inflater.inflate(R.layout.listitem1, parent, false);holder1 = new viewHolder1();holder1.textView = (TextView)convertView.findViewById(R.id.textview1);holder1.checkBox = (CheckBox)convertView.findViewById(R.id.checkbox);convertView.setTag(holder1);
        break;
      case TYPE_2:convertView = inflater.inflate(R.layout.listitem2, parent, false);holder2 = new viewHolder2();holder2.textView = (TextView)convertView.findViewById(R.id.textview2);holder2.imageView = (ImageView)convertView.findViewById(R.id.imageview);convertView.setTag(holder2);
        break;}}
    else{
    //有convertView,按样式,取得不用的布局
      switch(type){
      case TYPE_1:holder1 = (viewHolder1) convertView.getTag();
        break;
      case TYPE_2:holder2 = (viewHolder2) convertView.getTag();
        break;}
      //设置资源
      switch(type){
      case TYPE_1:holder1.textView.setText(Integer.toString(position));holder1.checkBox.setChecked(true);
        break;
      case TYPE_2:holder2.textView.setText(Integer.toString(position));holder2.imageView.setBackgroundResource(R.drawable.icon);
        break;}}
    return convertView;}
}
//各个布局的控件资源
class viewHolder1{CheckBox checkBox;TextView textView;
}
class viewHolder2{ImageView imageView;TextView textView;
}

  以上基本就是主要的内容了,下面再补充实际操作当中的一些Tips
  *如果convertView上用Type区分有些繁琐,或者不需要那么复杂,只是很少有出现不同的情况,那么还可以在取得convertView后,通过java提供的instanceof来判断是否可以强转,如果不能强转,就去新建一个View的做法,但是其实这种做法并不规范,所以还是推荐上面的做法。
  *第二个是关于ListView,对于纯色的item背景,其实可以直接设置BackgroundColor,而不要使用图片,这一部分其实可以有不小的提升,同样的,对于任何纯色的背景,应该尽量去设置RGB颜色,而不是全用一张图片做背景返回。

5.在onItemClickLinstener()中监听点击事件,更改Item背景的问题

  我以前都是这么写的

mListView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {view.findViewById(R.id.background).setBackgroundResource(R.drawable.bg_seleted);}
});

  onItemClick()方法中第二个参数view就是mListView使用的adapter中,getView()方法返回的View,第三个参数position也对应getView()方法的position参数。这样写乍看之下没有任何问题,但是如果在adapter中使用了convertView来优化适配器,那么getView()方法返回的很可能是缓存的convertView。这似乎也没有问题,因为每次点击时的item必然已经加载在可见的位置。但是,如果要这样,我做了一个横向的list,如果要他来显示某些配置信息:

  如上图,点击item之后就会使背景变化(加上白色背景框了),再点击之后背景又会回到默认状态,而且一页最多显示5个item。好,现在我把第二个、第三个,以及第30个图标item点亮了,并把这些信息保存起来,以便下次进入程序时仍然让这几个item是点亮的状态。于是,下次进入程序时,初始化阶段我需要读取这些保存的信息,把需要点亮的图标item绘制出来。怎么做呢?我是这样做的:

mListView.getChildAt(idex).findViewById(R.id.background).setBackgroundResource(R.drawable.bg_seleted);

  好,问题来了,空指针!

  加载第二、三个item是没有问题的,因为进入程序时listview会首先加载最先显示的5个item,第6个呢,可能也会加载,这样滑动的时候才不至于措手不及。可是第30个就难说了。也许滑动到第二十多个的时候才会加载——到了需要使用的时候才加载,这是性能最优的选择。但是在初始化的时候mListView.getChild(30)返回就是null,不好意思,程序崩了……

  怎么办呢?

  被领导骂了无数次之后,终于认识到不能用上面那种语句。先回过头来,看一下本文第3个标题下的那个典型BaseAdapter写法,就是那个MarkerItemAdapter,它的构造中传入了一个List<MarkerItem>,这个就是通常要显示在listView里的数据表,在MarkerItem类里曾加一条属性吧,比如int state = 0;在onItemClick()里改变state的值:

markerItems.get(position).setState(1);

  然后在adapter的getView()方法中根据item的state属性来加载对应的背景即可。

@Overridepublic View getView(int position, View convertView, ViewGroup parent){ViewHolder viewHolder = null;if (null == convertView){viewHolder = new ViewHolder();LayoutInflater mInflater = LayoutInflater.from(mContext);convertView = mInflater.inflate(R.layout.item_marker_item, null);viewHolder.name = (TextView) convertView.findViewById(R.id.name);viewHolder.description = (TextView) convertView.findViewById(R.id.description);viewHolder.createTime = (TextView) convertView.findViewById(R.id.createTime);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}// set item values to the viewHolder:
MarkerItem markerItem = getItem(position);if (null != markerItem){viewHolder.name.setText(markerItem.getName());viewHolder.description.setText(markerItem.getDescription());viewHolder.createTime.setText(markerItem.getCreateDate());if (markerItem.getState() == 0) {holder.bgLayout.setBackgroundResource(R.drawable.bg_default);} else if (markerItem.getState() == 1) {holder.bgLayout.setBackgroundResource(R.drawable.bg_seleted);}}return convertView;}

  这样,到了需要加载的时候才加载,而且能加载出正确的数据。所以在操作有适配器的组件时,务必直接更改自己的数据集,而不是更改listView等的itemView才是最稳妥的做法。最后不要忘了在点击事件最后通知adapter更新:

mAdapter.notifyDataSetChanged();

  多么痛的领悟……

  参考博文:1.http://blog.chinaunix.net/uid-11798215-id-3407345.html

2.http://mzh3344258.blog.51cto.com/1823534/889879

3.http://www.cnblogs.com/over140/archive/2011/03/23/1991100.html

4.http://www.cnblogs.com/mengdd/p/3254323.html

转载于:https://www.cnblogs.com/Couch-potato/p/3723421.html

android BaseAdapter优化相关推荐

  1. android baseadapter优化,2.4.6 BaseAdapter优化

    本节引言: 上一节中我们学习了如何来使用一个ListView以及自定义一个简单的BaseAdapter,我们从代码 中可以看出比较重要的两个方法:getCount()和getView(),界面上有多少 ...

  2. Android基础入门教程——2.4.3 BaseAdapter优化

    Android基础入门教程--2.4.3 BaseAdapter优化 标签(空格分隔): Android基础入门教程 本节引言: 上一节中我们学习了如何来使用一个ListView以及自定义一个简单的B ...

  3. Android入门教程三十六之BaseAdapter优化

    上一节中我们学习了如何来使用一个ListView以及自定义一个简单的BaseAdapter,我们从代码 中可以看出比较重要的两个方法:getCount()和getView(),界面上有多少列就会调用多 ...

  4. Android BaseAdapter与ListView的使用

    Android BaseAdapter与ListView的使用 定义:BaseAdapterextends Objectimplements ListAdapter SpinnerAdapter Ba ...

  5. Android 系统性能优化(42)---Android代码内存优化建议-Android资源篇

    Android代码内存优化建议-Android资源篇 这篇文章主要介绍在实际Android应用程序的开发中,容易导致内存泄露的一些情况.开发人员如果在进行代码编写之前就有内存泄露方面的基础知识,那么写 ...

  6. Android 系统性能优化(37)---Android内存优化之一:MAT使用入门

    Android内存优化之一:MAT使用入门 MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速.功能丰富的JAVA heap分析工具,它可以帮助我们查 ...

  7. Android 内存优化(1)

    不少人认为JAVA程序,因为有垃圾回收机制,应该没有内存泄露.其实如果我们一个程序中,已经不再使用某个对象,但是因为仍然有引用指向它,垃圾回收器就无法回收它,当然该对象占用的内存就无法被使用,这就造成 ...

  8. ANDROID内存优化(大汇总——中)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...

  9. Android内存优化(三)避免可控的内存泄漏

    相关文章 Android性能优化系列 Java虚拟机系列 前言 内存泄漏向来都是内存优化的重点,它如同幽灵一般存于我们的应用当中,有时它不会现身,但一旦现身就会让你头疼不已.因此,如何避免.发现和解决 ...

最新文章

  1. python中平均值函数_python自定义函数ma(x,y)求简单平均值输出结果到列表
  2. wikioi 3027 线段覆盖 2
  3. 安装网关报mysql服务ini_linux 操作系统下ORACLE数据库使用透明网关连接MYSQL
  4. python转义符个数,python(五)——运算符、字符串、转义字符
  5. JDK8新特性之Lambda表达式
  6. proxomitron 个人代理工具
  7. 企业为什么需要IT资产管理
  8. 如何自学并且系统学习计算机网络?(知乎问答)
  9. GNU make manual 翻译(三十)
  10. c语言科学计数法输出1_e10,北航13年机试--十进制数字的科学计数法表示的C语言实现...
  11. 同样磁盘数,不同raid级别的随机IO性能差异对比
  12. (五)谷歌地图坐标转换:经纬度如何实现转换成谷歌地图平面坐标
  13. 手机当台式电脑摄像头
  14. HDOJ 4889 Scary Path Finding Algorithm 颠覆spfa slf优化
  15. ESXi 6.7 封装驱动(Intel-I219V使用非vib的离线包驱动格式)
  16. i春秋web-Backdoor(.git泄露、vim备份泄露、代码审计)
  17. 【Kickstart】2019 Round A - Parcels
  18. AngularJS PrimeNG 的 自定义排序customSort
  19. 微信开放平台 公众号第三方平台开发 教程三 一键登录授权给第三方平台
  20. python绘制地图线路_python pyecharts绘制地图

热门文章

  1. ORACLE SQL分组查询某列或某几列重复信息数量
  2. arp 原理及查杀方式
  3. 测试局域网路的MTU最大值
  4. GDAL2.x与1.x的主要变化比较(以C++为例说明)
  5. .net core 多平台开发体验
  6. C#图片处理之:最简单的柔化算法
  7. mac mysql php_Mac下搭建PHP开发环境(Apache+PHP+MySQL+phpMyAdmin)
  8. 三容水箱液位控制系统_光电液位传感器在饮水机中的应用解决方案
  9. python修改txt里面的内容_python修改txt部分内容,并保存。
  10. lstm 输入数据维度_理解Pytorch中LSTM的输入输出参数含义