[Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染
以下内容为原创,转载请注明:
来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3992843.html
本文讲的工具均放在AndroidBucket开源项目中,欢迎大家star/fork,地址:https://github.com/wangjiegulu/AndroidBucket
主要实现聊天功能中的发送不同类型的信息,比如纯文本、图片、语音、图文混排多媒体的数据等(具体效果看微信)。
这里使用AdapterTypeRender在BaseTypeAdapter(这个之后会讲到)中实现。
这里主要的实现方式是在ChatAdapter(继承BaseTypeAdapter)中根据每个position的item的type,来使用不同的AdapterTypeRender渲染器进行渲染。渲染的过程当然是在getView方法中进行。
1. AdapterTypeRender
先来看看AdapterTypeRender这个接口。它有3个方法:getConvertView()、fitEvents()、fitDatas()三个方法。
package com.wangjie.androidbucket.adapter; import android.view.View; /**
* 用于对不同类型item数据到UI的渲染
* Author: wangjie
* Email: tiantian.china.2@gmail.com
* Date: 9/14/14.
*/
public interface AdapterTypeRender { /**
* 返回一个item的convertView,也就是BaseAdapter中getView方法中返回的convertView
* @return
*/
View getConvertView(); /**
* 填充item中各个控件的事件,比如按钮点击事件等
*/
void fitEvents(); /**
* 对指定position的item进行数据的适配
* @param position
*/
void fitDatas(int position); }
-getconvertView()方法用于返回给BaseTypeAdapter一个convertView,一个AdapterTypeRender实现类对应一个convertView实例,该AdapterTypeRender可以被重用,所以convertView也可以被重用了。
-fitEvents()方法用于给当前的item中的各个控件注册事件,比如点击事件、touch事件等(具体的注册事件后面回讲到),因为这个方法是在getView中只有convertView为null时才会调用,所以只会调用一次,所以在这里添加事件是比较好的。
-fitDatas()方法用于把数据适配到item的各个view中进行显示。这个方法只要getView得到调用,就会被调用。
2. BaseTypeAdapter
这是一个抽象类,是继承于BaseAdapter的,重写了里面的getView方法。会自动根据指定position的item获取对应的type,然后通过type实例化一个AdapterTypeRender的实现,然后又使用了BaseAdapter中自带的convertView的重用机制进行对view的重用,同样也是对AdapterTypeRender的重用。
package com.wangjie.androidbucket.adapter.typeadapter; import android.annotation.TargetApi;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.wangjie.androidbucket.R; /**
* Author: wangjie
* Email: tiantian.china.2@gmail.com
* Date: 9/25/14.
*/
public abstract class BaseTypeAdapter extends BaseAdapter{
@TargetApi(Build.VERSION_CODES.DONUT)
@Override
public View getView(int position, View convertView, ViewGroup parent) {
AdapterTypeRender typeRender;
if(null == convertView){
typeRender = getAdapterTypeRender(position);
convertView = typeRender.getConvertView();
convertView.setTag(R.id.ab__id_adapter_item_type_render, typeRender);
typeRender.fitEvents();
}else{
typeRender = (AdapterTypeRender) convertView.getTag(R.id.ab__id_adapter_item_type_render);
}
convertView.setTag(R.id.ab__id_adapter_item_position, position); if(null != typeRender){
typeRender.fitDatas(position);
} return convertView;
} /**
* 根据指定position的item获取对应的type,然后通过type实例化一个AdapterTypeRender的实现
* @param position
* @return
*/
public abstract AdapterTypeRender getAdapterTypeRender(int position);
}
为了实现AdapterTypeRender的重用,一旦生成了一个AdapterTypeRender实现类的实例,则使用setTag的方法进行对convertView和AdapterTypeRender的绑定(R.id.ab__id_adapter_item_type_render这个id是在AndroidBucket中定义了的),这个可以参考以前的ViewHolder的写法。
为了实现在同一个item中的事件(这里以view的点击事件为例)响应都共用一个观察者的实例,需要在convertView中保存对应的position。这是因为同一个convertView因为使用了view的重用,是被非显示页面的很多个item所共用的。所以只需要,在当前显示的一屏中,每个convertView对应的postion即可,毕竟这些事件触发只有在当前显示的一屏中才会被触发。保存postion的方式依然使用了setTag的方式(R.id.ab__id_adapter_item_position这个id是在AndroidBucket中定义了的),注意:子类一般不需要重写getView方法了,其他的数据适配UI渲染都交给Render吧!
除了重写了getView方法之外,还定义了一个抽象方法:getAdapterTypeRender。这个方法需要子类去实现,需要告诉BaseTypeAdapter,指定position的item,它的type对应的AdapterTypeRender的实例是什么。
3. 自定义AdapterTypeRender的实现
接下来,就尝试自己实现几个不同布局的Render吧。这里假设需要实现两种:文本布局(TypeTextRender)、图片布局(TypeImageRender)。
a) TypeTextRender的实现:
package com.wangjie.activities.typerendertest.adapter; import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.wangjie.androidbucket.adapter.typeadapter.AdapterTypeRender;
import com.wangjie.androidbucket.adapter.listener.OnConvertViewClickListener;
import com.wangjie.androidbucket.thread.ThreadPool;
import com.wangjie.androidbucket.utils.ABTimeUtil;
import com.wangjie.androidbucket.utils.ABViewUtil;
import com.wangjie.imageloadersample.imageloader.ImageLoader; /**
* Author: wangjie
* Email: tiantian.china.2@gmail.com
* Date: 9/14/14.
*/
public class TypeTextRender implements AdapterTypeRender {
private Context context;
private ChatAdapter adapter;
private View contentView; public TypeTextRender(Context context, ChatAdapter adapter) {
this.context = context;
this.adapter = adapter;
// 解析文本类型的布局
contentView = LayoutInflater.from(context).inflate(R.layout.item_type_text, null);
} @Override
public View getConvertView() {
// 返回文本类型的布局
return contentView;
} /**
* 这个方法同一个convertView只会被调用一次,所以可以放心地在这里执行事件地绑定,不用担心生成过多的OnClickListener等
*/
@Override
public void fitEvents() {
/**
* 生成一个在convertView中使用的clickListener
*/
OnConvertViewClickListener onConvertViewClickListener = new OnConvertViewClickListener(contentView, R.id.ab__id_adapter_item_position) {
@Override
public void onClickCallBack(View registedView, int... positionIds) {
ChatAdapter.OnChatItemListener onChatItemListener = adapter.getOnChatItemListener();
switch (registedView.getId()) {
case R.id.item_type_text_view:
if (null != onChatItemListener && null != positionIds && positionIds.length > 0) {
onChatItemListener.onItemClicked(positionIds[0]);
}
break; case R.id.item_type_text_head_iv:
if (null != onChatItemListener && null != positionIds && positionIds.length > 0) {
onChatItemListener.onHeadClicked(positionIds[0]);
}
break; } }
}; // 通过ABViewUtil从contentView中获取对应id的控件,然后设置OnClickListener
ABViewUtil.obtainView(contentView, R.id.item_type_text_view)
.setOnClickListener(onConvertViewClickListener);
ABViewUtil.obtainView(contentView, R.id.item_type_text_head_iv)
.setOnClickListener(onConvertViewClickListener); } private ImageView headIv;
private View rootView;
private TextView contentTv; @Override
public void fitDatas(int position) {
// 通过ABViewUtil从contentView中获取对应id的控件,然后设置OnClickListener
headIv = ABViewUtil.obtainView(contentView, R.id.item_type_text_head_iv);
contentTv = ABViewUtil.obtainView(contentView, R.id.item_type_text_content_tv);
/**
* 在这里适配数据到ui
*/
Message message = adapter.getItem(position);
contentTv.setText(message.getContent());
ImageLoader.getInstances().displayImage(message.getHeadUrl(), headIv, 100, null, R.drawable.default_head); } }
如上代码,该TypeTextRender实现了AdapterTypeRender接口,实现了其中的3个方法。注意:需要在构造方法中解析出convertView,用于提供给BaseTypeAdapter在getView中返回。
然后在fitEvents中注册各种事件,这里只注册了点击事件(一个rootView、一个headIv注册了点击事件)。这里的OnConvertViewClickListener是一个实现了View.OnClickListener的一个抽象类,生成一个OnConvertViewClickListener时需要传入convertView和positions的id,这样由于Render被重用后,convertView也是被重用了,导致onClickListener也是被重用了,这会导致响应点击事件的时候,回调的onClicked方法中无法得知点击的是哪个View,所以,需要把converView和positions的id传入,positions的id可以用来在convertView中绑定postion作为tag。positonsIds是一个可变长的参数,因为可能是一个ExpandableListView,需要groupPosition和childPosition两个positon来确定。回调的onClickCallBack中的参数registedView表示被点击的view,positionIds,被点击的item的positions(这个也是可以在AndroidBucket中找到,以后有时间会针对这个详细说明)。
“ABViewUtil.obtainView...”这个方法对viewHolder进行了封装,把convertView中的控件都缓存在了一个SparseArray<View>中(作用跟常用的ViewHolder相同)。
然后在fitDatas方法中就可以进行对数据的适配了,相当于我们以前在BaseAdapter的getView方法中的操作了。
b) TypeImageRender的实现(与TypeTextRender大同小异,不做过多的说明了):
package com.wangjie.activities.typerendertest.adapter; import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.wangjie.androidbucket.adapter.typeadapter.AdapterTypeRender;
import com.wangjie.androidbucket.adapter.listener.OnConvertViewClickListener;
import com.wangjie.androidbucket.thread.ThreadPool;
import com.wangjie.androidbucket.utils.ABTimeUtil;
import com.wangjie.androidbucket.utils.ABViewUtil;
import com.wangjie.imageloadersample.imageloader.ImageLoader; /**
* Author: wangjie
* Email: tiantian.china.2@gmail.com
* Date: 9/14/14.
*/
public class ChatTypeImageRender implements AdapterTypeRender {
private Context context;
private ChatAdapter adapter;
private View contentView; public ChatTypeImageRender(Context context, ChatAdapter adapter) {
this.context = context;
this.adapter = adapter;
// 解析图片类型的布局
contentView = LayoutInflater.from(context).inflate(R.layout.item_type_text, null);
} @Override
public View getConvertView() {
// 返回文本类型的布局
return contentView;
} /**
* 这个方法同一个convertView只会被调用一次,所以可以放心地在这里执行事件地绑定,不用担心生成过多的OnClickListener等
*/
@Override
public void fitEvents() {
/**
* 生成一个在convertView中使用的clickListener
*/
OnConvertViewClickListener onConvertViewClickListener = new OnConvertViewClickListener(contentView, R.id.ab__id_adapter_item_position) {
@Override
public void onClickCallBack(View registedView, int... positionIds) {
ChatAdapter.OnChatItemListener onChatItemListener = adapter.getOnChatItemListener();
switch (registedView.getId()) {
case R.id.item_type_text_view:
if (null != onChatItemListener && null != positionIds && positionIds.length > 0) {
onChatItemListener.onItemClicked(positionIds[0]);
}
break; case R.id.item_type_text_head_iv:
if (null != onChatItemListener && null != positionIds && positionIds.length > 0) {
onChatItemListener.onHeadClicked(positionIds[0]);
}
break;
case R.id.item_type_text_content_iv:
if (null != onChatItemListener && null != positionIds && positionIds.length > 0) {
onChatItemListener.onImageClicked(positionIds[0]);
}
break; } }
}; // 通过ABViewUtil从contentView中获取对应id的控件,然后设置OnClickListener
ABViewUtil.obtainView(contentView, R.id.item_type_text_view)
.setOnClickListener(onConvertViewClickListener);
ABViewUtil.obtainView(contentView, R.id.item_type_text_head_iv)
.setOnClickListener(onConvertViewClickListener);
ABViewUtil.obtainView(contentView, R.id.item_type_text_content_iv)
.setOnClickListener(onConvertViewClickListener); } private ImageView headIv;
private View rootView;
private ImageView contentIv; @Override
public void fitDatas(int position) {
// 通过ABViewUtil从contentView中获取对应id的控件,然后设置OnClickListener
headIv = ABViewUtil.obtainView(contentView, R.id.item_type_text_head_iv);
contentIv = ABViewUtil.obtainView(contentView, R.id.item_type_text_content_iv);
/**
* 在这里适配数据到ui
*/
Message message = adapter.getItem(position);
ImageLoader.getInstances().displayImage(message.getHeadUrl(), headIv, 100, null, R.drawable.default_head);
ImageLoader.getInstances().displayImage(message.getContentUrl(), headIv, 100, null, R.drawable.default_pic); } }
3. BaseTypeAdapter的实现
到这里,我们已经定义好了各种type的Render了,现在需要在Adapter中去使用它,方法之前讲过,只要继承BaseTypeAdapter,然后实现里面的getAdapterTypeRender方法即可:
package com.wangjie.activities.typerendertest.adapter; import android.content.Context;
import com.wangjie.androidbucket.adapter.typeadapter.AdapterTypeRender;
import com.wangjie.androidbucket.adapter.typeadapter.BaseTypeAdapter; import java.util.List; /**
* Author: wangjie
* Email: tiantian.china.2@gmail.com
* Date: 9/14/14.
*/
public class MessageAdapter extends BaseTypeAdapter { public static interface OnChatItemListener{
void onImageClicked(int position);
void onHeadClicked(int position);
void onItemClicked(int position);
}
private OnChatItemListener onChatItemListener;
public void setOnChatItemListener(OnChatItemListener onChatItemListener) {
this.onChatItemListener = onChatItemListener;
}
public OnChatItemListener getOnChatItemListener() {
return onChatItemListener;
} private Context context;
private List<Message> list;
public List<Message> getList() {
return list;
} public MessageAdapter(Context context, List<Message> list) {
this.context = context;
this.list = list;
} @Override
public int getCount() {
return list.size();
} @Override
public DoctorFriendMessageViewModelProxy getItem(int position) {
return list.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public int getItemViewType(int position) {
return list.get(position).getTyp();
} @Override
public int getViewTypeCount() {
return 2;
} @Override
public AdapterTypeRender getAdapterTypeRender(int position){
AdapterTypeRender typeRender = null;
switch(getItemViewType(position)){
case MessageConstants.MessageType.IMAGE:
typeRender = new ChatTypeImageRender(context, this);
break;
case MessageConstants.MessageType.TEXT:
default:
typeRender = new ChatTypeTextRender(context, this);
break;
}
return typeRender;
} }
如上代码所示,通过实现getAdapterTypeRender来获取对应类型的Render即可了。
[Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染的更多相关文章
- 使用AdapterTypeRender对不同类型的item数据到UI的渲染
要实现聊天功能中的发送不同类型的信息,比如纯文本.图片.语音.图文混排多媒体的数据等(具体效果看微信). 这里使用AdapterTypeRender在BaseTypeAdapter(这个之后会讲到)中 ...
- ListView显示多种类型的item
ListView可以显示多种类型的条目布局,这里写显示两种布局的情况,其他类似 这是MainActivity:,MainActivity的布局就是一个ListView public class Mai ...
- Android限定EditText的输入类型为数字或者英文(包括大小写),EditText,TextView只能输入两位小数
Android限定EditText的输入类型为数字或者英文(包括大小写) // 监听密码输入框的输入内容类型,不可以输入中文 TextWatcher mTextWatcher = new Tex ...
- Android <Android应用开发实战> 资源类型<二>
1.菜单资源菜单不仅可以在onCreateContextMenu或onCreateOptionsMenu方法中通过代码创建,还可以在res/menu目录中建立相应的菜单资源文件,并在上面两个方法中加载 ...
- android自定义dialog中点击listview的item事件关闭dialog
import android.app.Activity; import android.app.AlertDialog; import android.app.AlertDialog.Builder; ...
- android 数据绑定(1)Ativity、Fragment、Item绑定数据源
1.简介 官方文档: https://developer.android.com/topic/libraries/data-binding 官方示例: https://github.com/andr ...
- Android通过HTTP协议实现上传文件数据
SocketHttpRequester.java package cn.itcast.utils; import java.io.BufferedReader; import java.io.Byte ...
- Android利用tcpdump和wireshark抓取网络数据包
Android利用tcpdump和wireshark抓取网络数据包 主要介绍如何利用tcpdump抓取andorid手机上网络数据请求,利用Wireshark可以清晰的查看到网络请求的各个过程包括三次 ...
- 【转】android笔记--保存和恢复activity的状态数据
一般来说, 调用onPause()和onStop()方法后的activity实例仍然存在于内存中, activity的所有信息和状态数据不会消失, 当activity重新回到前台之后, 所有的改变都会 ...
随机推荐
- Bootstrap Magic – 轻松创建自己的 Bootstrap 主题
Bootstrap Magic 是一款基于 Bootstrap 和 AngularJS 的主题创建工具.您可以轻松地创建您自己的 Twitter Bootstrap 主题,可以立即看到你的内容变化.您 ...
- jQuery.Data源码
jQuery.data的是jQuery的数据缓存系统.它的主要作用就是为普通对象或者DOM元素添加数据. 1 内部存储原理 这个原理很简单,原本要添加在DOM元素本身的数据,现在被集中的存储在cach ...
- APPSCAN使用外部浏览器
在使用appscan扫描时,自带浏览器可能存在兼容性问题(比如HTML5),故需要用到其他浏览器.在做139邮箱HTML5项目,需要使用chrome浏览器进行扫描.因此分享下如何使用外部浏览器,将之前 ...
- 一行代码,让你的应用中UIScrollView的滑动与侧滑返回并存
侧滑返回是iOS系统的一个很贴心的功能,特别是在大屏手机上,单手操作的时候去按左上角的返回键特别不方便.当我在使用一个APP的时候,如果控制器不能侧滑返回,我会觉得这个APP十分不友好...这款产品在 ...
- 使用Qt installer framework制作安装包
一.介绍 使用Qt库开发的应用程序,一般有两种发布方式:(1)静态编译发布.这种方式使得程序在编译的时候会将Qt核心库全部编译到一个可执行文件中.其优势是简单单一,所有的依赖库都集中在一起,其缺点也很 ...
- orleans/Documentation
福利 奥尔良的主要好处是︰开发人员工作效率,甚至为非专家程序员;和默认的透明可伸缩性与程序员没有特别努力.我们扩大每个下面这些好处. 开发人员的生产力 奥尔良的编程模型通过提供以下关键的抽象. 担保和 ...
- 解决 DotNetCore.1.0.1-VS2015Tools.Preview2.0.3.exe 在VS2015 Update3 安装失败的问题
今天抽空升级VS2015 Update3. 在安装DotNetCore.1.0.1-VS2015Tools.Preview2.0.3.exe 时报错了,看了错误日志 显示: 看到我标红的两个地方,那么 ...
- 孙鑫MFC学习笔记2:C++回顾
1.通常可以把实例与对象等同起来 2.函数重载是发生在同一个类中的 3.函数的覆盖是发生在父类与子类之间的(函数被覆盖后可以使用父类的类名加作用域操作符::) 4.含有纯虚函数的类叫抽象类,抽象类无法 ...
- Eclipse上GIT插件EGIT使用手册
http://blog.csdn.net/luckarecs/article/details/7427605 Eclipse上GIT插件EGIT使用手册 一_安装EGIT插件 http://dow ...
- Word, PPT和Excel的常用技巧(持续更新)
本文的目的是记录平时使用Word, PowerPoint和Excel的过程中的一些小技巧,用于提升工作效率. 此文会不定期的更新,更新频率完全取决于实际使用遇到的问题的次数. 目录 Word Powe ...