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

前言:对于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数组中,如果是,则将“关注”置为红色,否则置为初始色,黑色。




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/295630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

刚刚还在做菜,瞬间人就没了!厨房里一定不要再做这些事

全世界只有3.14 % 的人关注了爆炸吧知识厨房是一个家最有生活气息的地方&#xff0c;即使在外面&#xff0c;只要闻到别人家厨房飘出的饭菜香味&#xff0c;就很让人想家。但是&#xff0c;如果不注意细节&#xff0c;厨房也容易酿成悲剧&#xff01;今天&#xff0c;给大家看几…

poj 3125 Printer Queue(STL注意事项)

http://poj.org/problem?id3125 这道题没什么突出的地方&#xff0c;是一道很水的题&#xff0c;可以用list&#xff0c;也可以用queue来解决。&#xff08;用list解决的代码我就不写了&#xff09;把它写上来&#xff0c;只是因为我在使用STL的时候犯了一个小错误&#xff0c…

TCTDB存储结构

TCTDB是tokyo cabinet家族中的表格数据库&#xff08;如上图&#xff09;&#xff0c;其实现基于TCHDB&#xff08;hash database&#xff09;和TCBDB(B-tree database)。TCHDB参考&#xff1a;http://blog.chinaunix.net/space.php?uid20196318&doblog&id327754 TCBD…

Android之px 与 dp, sp换算公式

px: pixels(像素). 不同设备显示效果相同&#xff0c;一般我们HVGA代表320x480像素&#xff0c;这个用的比较多。 pt: point&#xff0c;是一个标准的长度单位&#xff0c;1pt&#xff1d;1/72英寸&#xff0c;用于印刷业&#xff0c;非常简单易用&#xff1b; sp: scaled pi…

05Prism WPF 入门实战 - Navigation

1.概要源码及PPT地址&#xff1a;https://github.com/JusterZhu/wemail视频地址&#xff1a;https://www.bilibili.com/video/BV1KQ4y1C7tg?share\sourcecopy\web本章分为以下三个部分来了解&#xff1a;Part1 视图导航、参数传递Part2 确认导航Part3 导航日志2.详细内容Part1…

明明没PS,看起来却像PS过的32张照片

全世界只有3.14 % 的人关注了爆炸吧知识现在PS太普遍&#xff0c;以至于人们看到不同寻常的东西&#xff0c;第一时间会怀疑经过技术处理。但也有一些照片&#xff0c;真的没有PS过&#xff01;1、这些轮胎痕迹令人产生了3D的错觉&#xff1a;2、截然分开的几种景观&#xff0c…

没有什么是日本牛郎店做不到的......

1 第一眼你看见了啥&#xff1f;▼2 确实很悲伤&#xff08;dy&#xff1a;李逍遥&#xff09;▼3 貌似真是这样▼4 我不爱打麻将咋办&#xff1f;▼5 短短十几个字&#xff0c;满满的江湖纷争▼6 少了男主的朋友不要轻易尝试▼7 哦吼▼8 双十一如你所愿&#xff0c;哈哈…

持续集成、持续交付(CI/CD)开篇,先来唠唠嗑

前言现在稍微有点规模的系统&#xff0c;很多都是采用分布式/微服务架构&#xff0c;将一个大系统拆分为很多个功能模块进行开发、测试、发布、管理等&#xff0c;如果全部流程都采用人工的形式进行的话&#xff0c;效率肯定是超级不高效滴。而且现在很多项目都采用极限编程的模…

100例经典炒菜_Python3经典100例(②)

实例6题目&#xff1a;斐波那契数列&#xff1b;分析&#xff1a;利用递归计算斐波那契数列&#xff0c;输入斐波那契数列的n位&#xff0c;调用递归计算出第n位的数列值&#xff1b;代码&#xff1a;#!/usr/bin/python3 # -*- coding: utf-8 -*- # Time : 2018-10-3 21:10 …

分享10个2012年最新发布的jQuery插件

为什么80%的码农都做不了架构师&#xff1f;>>> 日期&#xff1a;2012-5-9 来源&#xff1a;GBin1.com 本文收集了最新的jQuery社区发布的jQuery插件&#xff0c;绝对是你没有见过的&#xff0c;希望大家喜欢&#xff01; 1. SearchMeme 一个即时搜索的jQuery插件…

博士毕业的人也会交“智商税”?现实远比我们想象的残酷……

全世界只有3.14 % 的人关注了爆炸吧知识2018 年的那个多事之秋&#xff0c;我终于在人生快要过半的时候拿到了博士学位。大毛和小鱼是我博士期间玩得最好的兄弟&#xff0c;我们三人水平差不多&#xff0c;志向差不多&#xff0c;博士毕业后都进了双非高校。现实不像是想象中那…

聊一聊声明式接口调用与Nacos的结合使用

背景 对于公司内部的 API 接口&#xff0c;在引入注册中心之后&#xff0c;免不了会用上服务发现这个东西。现在比较流行的接口调用方式应该是基于声明式接口的调用&#xff0c;它使得开发变得更加简化和快捷。.NET 在声明式接口调用这一块&#xff0c;有 WebApiClient 和 Refi…

三次握手和四次挥手图解_三次握手和四次挥手简单理解

TCP三次握手TCP标志位TCP在其协议头中使用大量的标志位或者说1位&#xff08;bit&#xff09;布尔域来控制连接状态&#xff0c;一个包中有可以设置多个标志位。TCP是主机对主机层的传输控制协议&#xff0c;提供可靠的连接服务&#xff0c;采用三次握手确认建立一个连接&#…

Android之基于BaseAdapter和SimpleAdapter的GridView

GridView 第一种方式:用SimpleAdapter 先来贴出本文代码运行的结果: 本文需要添加/修改3个文件:main.xml、night_item.xml、JAVA源代码。 main.xml源代码如下,本身是个GirdView,用于装载Item: <?xml version="1.0" encoding="utf-8"?> <…

IOS开发CAKeyframeAnimation的基本使用与keypath的列举

2019独角兽企业重金招聘Python工程师标准>>> CAKeyframeAnimation跟CABasicAnimation的区别是&#xff1a;CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue)&#xff0c;而CAKeyframeAnimation会使用一个NSArray保存这些数值 - (void)value {CAKe…

少女为什么会身上香香的?

1 一旦接受这个设定苏打窃瓦辛格&#xff08;via.煎甜担担面&#xff09;▼2 有被冒犯到▼3 哈哈哈哈哈▼4 就差了难忘今宵▼5 还是挺在理的▼6 欣慰中带着点点悲伤▼7 原来是你的错▼8 哈哈哈哈哈▼你点的每个赞&#xff0c;我都认真当成了喜欢

Android之Android Studio三种方式导入外部资源 以及 报错处理

Android Studio三种方式导入外部资源 以及 报错处理 android studio提供了三种方式导入外部资源: 1. Library dependency – 在线添加、需联网下载 、 2. File dependency – 从本地添加一些jar包(要先将jar包</

android飞翔的小鸟游戏素材包_开心消消乐×愤怒的小鸟:为开心而战

手机里总有那么一些游戏&#xff0c;是你一旦不小心打开&#xff0c;就完全停不下来的。在这份“一直玩一直爽游戏清单”里&#xff0c;绝对少不了开心消消乐和愤怒的小鸟的身影。神奇的是&#xff0c;在2020的夏天&#xff0c;它们合体了&#xff01;在接到开心消消乐的brief时…

RabbitMQ 处理过慢,原来是一个 SQL 缓存框架导致的 GC 频繁触发

一&#xff1a;背景 1. 讲故事上个月底&#xff0c;有位朋友微信找到我&#xff0c;说他的程序 多线程处理 RabbitMQ 时过慢&#xff0c;帮忙分析下什么原因&#xff0c;截图如下&#xff1a;这问题抛出来&#xff0c;有点懵逼&#xff0c;没说CPU爆高&#xff0c;也没说内存泄…

3部世界顶级宇宙纪录片,献给对宇宙万物充满好奇的你~

全世界只有3.14 % 的人关注了爆炸吧知识宇宙深邃美丽&#xff0c;是黑夜的荧光&#xff0c;是夏天里冒着凉气的西瓜&#xff0c;总是诱人地勾起一代又一代人探索的欲望。对于宇宙思索与探索&#xff0c;人类的脚步从未停止。正是人类对宇宙的好奇&#xff0c;撑起了人类发展的大…