前言:对于listView的BaseAdapter的派生,难度比较大。最难理解的莫过于getView(int position, View convertView, ViewGroup parent)这个函数是如何产生每条记录的,有些博客中利用holderView,有些博客却没有用,种种方法之间有什么异同,今天我们就来揭开这个绘制ITEM机制的面纱。

本篇借助《PullToRefresh使用详解(二)---重写BaseAdapter实现复杂XML下拉刷新》的例子。所以本篇对于代码的讲解就比较粗略,如果有读者对于如何重写BaseAdapter不太熟悉的话,请先移步看看这篇文章,然后再回来这里,相信会有不一样的收获。

一、ConvertView回收机制

工作原理:

1、ListView 针对List中每个item,要求 adapter “给我一个视图” (getView)。
2、一个新的视图被返回并显示

如果我们有上亿个项目要显示怎么办?为每个项目创建一个新视图?NO!这不可能!
实际上Android为你缓存了视图。Android中有个叫做Recycler的构件,下图是他的工作原理:

如果你有10亿个项目(item),其中只有可见的项目存在内存中,其他的在Recycler中。
ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的。
当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图。   以上摘自《ListView中getView的原理+如何在ListView中放置多个item》

也就是说:

1、android的listView在初始化的时候,如上面这个列表,整个屏幕只能放下7个item,那么listView在初始化时,就会只创建7个view,对于这些view也就是参数中的convertView。
2、那问题来了,当继续网上滑动,item1消失了,而item8出来了。那系统还是为item8重新创建一个新的convertView吗?另一个问题,item1的convertView去哪了?(销毁回收资源,还是重新利用?)如果你是系统设计者,你会怎么做?大家想想,如果为每个要显示的item都创建新convertView是不是太浪费了,况且对于item1的convertView已经没用了,我们何不把它拿来给item8用。对!系统就是这样做的!这就是convertView的回收机制。就是将那些不再被用的ITEM的convertView重新给即将显示的ITEM使用的机制!

二、例子

先给大家看一下单个ITEM的布局图片,对于具体布局代码,看源码吧。

对于JAVA源码,我们先看这种方式写的convertView的生成方法。

package com.example.try_pulltorefresh_map;
/*** 完成了从TXT文本中提取,并向下刷新* blog:http://blog.csdn.net/harvic880925* @author harvic* @date  2014-5-8* */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONArray;import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshListView;
import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;import android.os.Bundle;
import android.app.ListActivity;
import android.content.Context;
import android.graphics.Color;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;public class MainActivity extends ListActivity {private ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();private PullToRefreshListView mPullRefreshListView;MyAdapter adapter=null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);//设定下拉监听函数mPullRefreshListView.setOnRefreshListener(new OnRefreshListener<ListView>() {@Overridepublic void onRefresh(PullToRefreshBase<ListView> refreshView) {String label = DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(),DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL);// Update the LastUpdatedLabelrefreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label);// Do work to refresh the list here.}});mPullRefreshListView.setMode(Mode.PULL_FROM_END);//设置底部下拉刷新模式listItem=getData();//获取LIST数据adapter = new MyAdapter(this);//设置适配器ListView actualListView = mPullRefreshListView.getRefreshableView();actualListView.setAdapter(adapter);  }private ArrayList<HashMap<String, Object>> getData() {ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();HashMap<String, Object> map = new HashMap<String, Object>();InputStream inputStream;try {inputStream=this.getAssets().open("my_home_friends.txt");String json=readTextFile(inputStream);JSONArray array = new JSONArray(json);for (int i = 0; i < array.length(); i++) {map = new HashMap<String, Object>();map.put("name", array.getJSONObject(i).getString("name"));map.put("info", array.getJSONObject(i).getString("info"));map.put("img",array.getJSONObject(i).getString("photo"));list.add(map);}return list;  } catch (Exception e) {// TODO: handle exceptione.printStackTrace();}return list;   }public final class ViewHolder{public TextView name;public TextView info;public TextView attentntion;}      public class MyAdapter extends BaseAdapter{private LayoutInflater mInflater;public MyAdapter(Context context){this.mInflater = LayoutInflater.from(context);}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn listItem.size();}@Overridepublic Object getItem(int arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic long getItemId(int arg0) {// TODO Auto-generated method stubreturn 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {System.out.println("position:"+position+"   convertView:"+convertView);ViewHolder holder = null;holder=new ViewHolder();        convertView = mInflater.inflate(R.layout.item, null);holder.name = (TextView)convertView.findViewById(R.id.name);holder.info = (TextView)convertView.findViewById(R.id.info);holder.attentntion=(TextView)convertView.findViewById(R.id.attention);holder.name.setText((String)listItem.get(position).get("name"));holder.info.setText((String)listItem.get(position).get("info"));final TextView attention=holder.attentntion;holder.attentntion.setOnClickListener(new View.OnClickListener() {              @Overridepublic void onClick(View v) {// TODO Auto-generated method stubattention.setTextColor(Color.RED);}});convertView.setTag(holder);      return convertView;}}工具类/*** * @param inputStream* @return*/public String readTextFile(InputStream inputStream) {String readedStr = "";BufferedReader br;try {br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));String tmp;while ((tmp = br.readLine()) != null) {readedStr += tmp;}br.close();inputStream.close();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return readedStr;}}

以上代码凡是懂如何派生自BaseAdapter的应该都可以看懂,这里就不再多讲,只看核心代码,摘录如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {System.out.println("position:"+position+"   convertView:"+convertView);ViewHolder holder = null;holder=new ViewHolder();        convertView = mInflater.inflate(R.layout.item, null);holder.name = (TextView)convertView.findViewById(R.id.name);holder.info = (TextView)convertView.findViewById(R.id.info);holder.attentntion=(TextView)convertView.findViewById(R.id.attention);holder.name.setText((String)listItem.get(position).get("name"));holder.info.setText((String)listItem.get(position).get("info"));final TextView attention=holder.attentntion;holder.attentntion.setOnClickListener(new View.OnClickListener() {              @Overridepublic void onClick(View v) {// TODO Auto-generated method stubattention.setTextColor(Color.RED);}}); return convertView;
}

这里利用system.out.println对convertView进行捕捉,运行如果如下:

手机初始化是这样子的:

根据上面我们讲的理论,在初始化时,整个屏幕能放下三个ITEM,所以会创建三个全新的convertView。当我往下拉一个ITEM,出现第四个ITEM的时候,就会回收第一个ITEM的convertView给第四个。捕捉结果如下:

清楚的看到,前四个convertView为NULL,当第五个ITEM出现时,此时由于第一个ITEM肯定已经滚出屏幕,所以将其重新传给即将出现的item5使用。我们上面说的第四个ITEM出现的时候就应该不再创建新convertView了,我想android开发者在考虑多创建一个ITEM的目的在于更安全吧。
回到上面的代码,好像看着代码没有任何问题,我在里面写了个clickListener,当点击“关注”的时候,字体会变红,试一下。
            点击“关注”                     下拉后再拉回来

问题出现了:当拉回来的时候,“关注”不再红了!!!!!!为什么????
问题出在代码上:

holder=new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
holder.name = (TextView)convertView.findViewById(R.id.name);
holder.info = (TextView)convertView.findViewById(R.id.info);
holder.attentntion=(TextView)convertView.findViewById(R.id.attention);

每次运行getView获取当前ITEM时,都会重新new 一个viewHolder与R.layout.item绑定,也就是说,每次都会产生一个新布局赋值给convertView让其显示。而我们上面讲了,android会将回收过来的convertView返回给即将显示的getView使用,以节约资源。而我们这里却没有领情,每次都重新创建一个布局赋给convertView,由于每次都创建一个新布局,所以当ITEM1被重新拉回来显示的时候,由于是重新创建的布局,当然是初始状态。“关注”当然也就是黑色的了。

改进

@Override
public View getView(int position, View convertView, ViewGroup parent) {System.out.println("position:"+position+"   convertView:"+convertView);ViewHolder holder = null;if (convertView == null) {holder=new ViewHolder();    convertView = mInflater.inflate(R.layout.item, null);holder.name = (TextView)convertView.findViewById(R.id.name);holder.info = (TextView)convertView.findViewById(R.id.info);holder.attentntion=(TextView)convertView.findViewById(R.id.attention);convertView.setTag(holder);      }else {holder = (ViewHolder)convertView.getTag();}holder.name.setText((String)listItem.get(position).get("name"));holder.info.setText((String)listItem.get(position).get("info"));final TextView attention=holder.attentntion;holder.attentntion.setOnClickListener(new View.OnClickListener() {              @Overridepublic void onClick(View v) {// TODO Auto-generated method stubattention.setTextColor(Color.RED);}});return convertView;
}   

不同的部分在这:

ViewHolder holder = null;if (convertView == null) {holder=new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
holder.name = (TextView)convertView.findViewById(R.id.name);
holder.info = (TextView)convertView.findViewById(R.id.info);
holder.attentntion=(TextView)convertView.findViewById(R.id.attention);
convertView.setTag(holder);
}else {holder = (ViewHolder)convertView.getTag();
}

当convertView为空,即初始化创建时,我们就将生成的布局利用setTag()保存在convertView中,当convertView利用回收机制回收过来让我们再次使用时,我们通过getTag()将保存的布局取出来,重新将布局里的各个控件重新赋值就可以了。这里就利用了android-listView的回收机制。
再看"关注"的点击事件运行的怎样:

看第二张图,当拉下去再拉回来的时候,一切正常,但当我们再往下拉(第三张图),问题又出现了,明明没有点P-5,为什么关注反而是红色的!!!!!!

这是因为,P-5用的是P-1回收来的convertView!!!而P-1的convertView的布局里“关注”是红色的。所以只要回收机制在,我们就没有办法改变从P-1回收来的convertView里的图片布局,除非人为的将其重置!

理解了这个问题以后,我们想想解决办法。
首先申请一个arrayList  attentionArr变量,保存用户点击“关注”的ITEM的position,然后在绘制当前ITEM时,根据这个position是否在attentionArr里来判断是不是将“关注”重新变红。

代码如下:

@Override
public View getView(final int position, View convertView, ViewGroup parent) {System.out.println("position:"+position+"   convertView:"+convertView);ViewHolder holder = null;if (convertView == null) {holder=new ViewHolder();      convertView = mInflater.inflate(R.layout.item, null);holder.name = (TextView)convertView.findViewById(R.id.name);holder.info = (TextView)convertView.findViewById(R.id.info);holder.attentntion=(TextView)convertView.findViewById(R.id.attention);convertView.setTag(holder);      }else {holder = (ViewHolder)convertView.getTag();}holder.name.setText((String)listItem.get(position).get("name"));holder.info.setText((String)listItem.get(position).get("info"));final TextView attention=holder.attentntion;//根据当前position判断,重新制做样式if (attentionArr.contains(position)) {attention.setTextColor(Color.RED);}else {attention.setTextColor(Color.BLACK);}holder.attentntion.setOnClickListener(new View.OnClickListener() {                @Overridepublic void onClick(View v) {// TODO Auto-generated method stubattention.setTextColor(Color.RED);attentionArr.add(position);//在点击时将position加入其中}});return convertView;
}

理解代码难度不大,首先在OnClickListener时,将position加入到attentionArr数组中,然后在getView里,判断当前position是不是用户点击过的,即是否包含在attentionArr数组中,如果是,则将“关注”置为红色,否则置为初始色,黑色。

Android之BaseAdapter—convertView回收机制与动态控件响应相关推荐

  1. Android进程优先级和垃圾回收机制

    进程优先级低到高: 空进程 后台进进程 服务进程 可见进程 前台进程 垃圾回收机制: 引用计数法(未采用),无法处理循环引用问题.egA引用B,B引用C,C引用A. 标注并清理回收法(mark and ...

  2. Android 操作系统的进程回收机制

    Android APP 的运行环境 Android 是一款基于 Linux 内核,面向移动终端的操作系统.为适应其作为移动平台操作系统的特殊需要,谷歌对其做了特别的设计与优化,使得其进程调度与资源管理 ...

  3. android 内存回收机制

    Android APP 的运行环境 Android 是一款基于 Linux 内核,面向移动终端的操作系统.为适应其作为移动平台操作系统的特殊需要,谷歌对其做了特别的设计与优化, 使得其进程调度与资源管 ...

  4. Android 操作系统的内存回收机制

    转自:http://www.ibm.com/developerworks/cn/opensource/os-cn-android-mmry-rcycl/index.html Android APP 的 ...

  5. Android 操作系统的内存回收机制。

    转载自品略网:http://www.pinlue.com/article/2020/03/0808/089994336918.html Android APP 的运行环境 Android 是一款基于 ...

  6. Android 操作系统的内存回收机制之默认内存回收、OOM以及lowmemorykiller

    Android 操作系统中的内存回收可分为两个层次,即默认内存回收与内核级内存回收,本章重点对默认内存回收机制进行研究,Linux 内核层次的内存回收机制本文不涉及. 本章所有代码可参见 Activi ...

  7. Android AdapterView 源码分析以及其相关回收机制的分析

    前言 忽然,发现,网上的公开资料都是教你怎么继承一个baseadapter,然后重写那几个方法,再调用相关view的 setAdpater()方法, 接着,你的item 就显示在手机屏幕上了.很少有人 ...

  8. android listview 资源回收,ListView的回收机制如何运作

    最初我还没有意识到listview回收和convertview使用机制,但经过一整天的研究,我通过引用android.amberfog中的图像非常了解列表视图的机制. 当过你的ListView充满适配 ...

  9. JAVA中的垃圾回收机制以及其在android开发中的作用

    http://blog.csdn.net/xieqibao/article/details/6707519 这篇文章概述了JAVA中运行时数据的结构,以及垃圾回收机制的作用.在后半部分,描述了如何检测 ...

最新文章

  1. 高效万进制——蓝桥杯|HDOJ 1002 大数加法——30行代码AC
  2. iio Engine logoHTML5 应用框架 iio Engine
  3. java对象不会被改变_Java 并发编程(二)对象的不变性和安全的公布对象
  4. Linux的实际操作:文件目录类的实用指令(rm mv)
  5. python遍历集合_Python 高效遍历 集合所有子集的全组合
  6. 别看微信,微博,头条用户都很多,自媒体作者也很多
  7. Webkit中textarea的设定
  8. java判断输入值是否在数组_java判断特定值是否在数组中的方法
  9. Kubernetes 小白学习笔记(21)--kubernetes的运维-管理Lable
  10. idea2020更新功能_idea2020 插件备份
  11. 计算机word设置信纸,一分钟教你学会用Word做信纸和公章!
  12. linux服务器端口的状态,Linux查看端口状态及关闭端口方法
  13. Pycharm破解(学习python的day01)
  14. 配置jetson的usb转串口驱动
  15. 阿里巴巴java工程师面试经验详情
  16. 学嵌入式需要报培训班吗?
  17. ecshop dwt替换为html,修改ecshop模板中lbi和dwt文件需知
  18. leetcode剑指offe刷题-第一题-用两个栈实现队列
  19. linux bash环境下面给expect脚本传递参数
  20. 自己整理的财务知识(财务比率)(英文不标准是为了自己建立自定义字段而已)

热门文章

  1. 高并发项目Java是标配?.NET Core要将它拉下“神坛”!
  2. Magicodes.IE 2.3重磅发布——.NET Core开源导入导出库
  3. 【译】探索更轻量的Electron替代品来托管Blazor桌面应用程序
  4. 【实战 Ids4】║ 又一个项目迁移完成(MVC)
  5. 依赖注入在 dotnet core 中实现与使用:1 基本概念
  6. 从严治码-别人在项目中下毒,我该怎么治?
  7. [开源] FreeSql AOP 功能模块 - FreeSql
  8. Docker最全教程之使用Docker搭建Java开发环境(十八)
  9. 如何优雅的利用Windows服务来部署ASP.NET Core程序
  10. 通过Chocolatey软件包管理器安装.NET Core