要实现聊天功能中的发送不同类型的信息,比如纯文本、图片、语音、图文混排多媒体的数据等(具体效果看微信)。

这里使用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)@Overridepublic 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);}@Overridepublic View getConvertView() {// 返回文本类型的布局return contentView;}/*** 这个方法同一个convertView只会被调用一次,所以可以放心地在这里执行事件地绑定,不用担心生成过多的OnClickListener等*/@Overridepublic void fitEvents() {/*** 生成一个在convertView中使用的clickListener*/OnConvertViewClickListener onConvertViewClickListener = new OnConvertViewClickListener(contentView, R.id.ab__id_adapter_item_position) {@Overridepublic 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的控件,然后设置OnClickListenerABViewUtil.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;@Overridepublic void fitDatas(int position) {        // 通过ABViewUtil从contentView中获取对应id的控件,然后设置OnClickListenerheadIv = 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);}@Overridepublic View getConvertView() {// 返回文本类型的布局return contentView;}/*** 这个方法同一个convertView只会被调用一次,所以可以放心地在这里执行事件地绑定,不用担心生成过多的OnClickListener等*/@Overridepublic void fitEvents() {/*** 生成一个在convertView中使用的clickListener*/OnConvertViewClickListener onConvertViewClickListener = new OnConvertViewClickListener(contentView, R.id.ab__id_adapter_item_position) {@Overridepublic 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的控件,然后设置OnClickListenerABViewUtil.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;@Overridepublic void fitDatas(int position) {        // 通过ABViewUtil从contentView中获取对应id的控件,然后设置OnClickListenerheadIv = 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;}@Overridepublic int getCount() {return list.size();}@Overridepublic DoctorFriendMessageViewModelProxy getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic int getItemViewType(int position) {return list.get(position).getTyp();}@Overridepublic int getViewTypeCount() {return 2;}@Overridepublic 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;}}

转载于:https://www.cnblogs.com/lianghe01/p/4227217.html

使用AdapterTypeRender对不同类型的item数据到UI的渲染相关推荐

  1. RecyclerView用法--展示多种类型Item数据

    如题,本文主要介绍RecyclerView的基本使用方法,像ListView一样展示多种类型的Item数据. 首先介绍一下实体类:ItemPO,用来表示每个Item代表的数据类型: package c ...

  2. RecycleView加载不同类型的Item

    前情提要:去面试一家外企,需求是要重复使用一个布局很多次,单单是布局的话用include就可以了,但是逻辑也要复用,而且提出了使用listview复用条目优化性能,想做成一个自定义控件.我尝试了下,感 ...

  3. Echarts pie 饼图类型后显示数据

    Echarts pie 饼图类型后显示数据 sysPie:function(libtypelist){var that=this;libtypelist=that.indexData.libtypel ...

  4. SAP MM 采购申请单据ITEM数据里的Closed标记

    SAP MM 采购申请单据ITEM数据里的Closed标记 笔者在SAP系统里发现,某个采购申请单据,已经全额数量的转成了采购订单, 其ITEM数据里的Closed标记并没有被勾选上,如下图: 我查了 ...

  5. 值类型与引用类型数据运算

    值类型与引用类型数据运算 ​ 值类型的数据和引用类型的数据进行运算的时候,会遵循如下步骤: 1.调用引用类型数据的valueOf方法,获取返回值,尝试和值类型的数据进行运算,如果可以计算,就得出结果. ...

  6. JSON文件的应用——记录类型的用户数据存储

    章节索引 前提 从问题出发 JSON和XML 专精JSON JSON文件读与写 (1)读JSON文件 (2)正向映射 (3)反向映射 (4)写JSON文件 后记 前提 之前一直有个问题没有弄清楚,就是 ...

  7. C语言指针类型和所指数据的类型要一致吗

    C语言指针类型和所指数据的类型必须要一致吗 网上很多答案是:"必须,不一致会出现语法错误",但是 no try no know. 首先看下我的环境: 下面是代码: #include ...

  8. mysql blob longblob_MYSQL教程Mysql LONGBLOB 类型存储二进制数据 (修改+调试+整理)

    <MYSQL教程Mysql LONGBLOB 类型存储二进制数据 (修改+调试+整理)>要点: 本文介绍了MYSQL教程Mysql LONGBLOB 类型存储二进制数据 (修改+调试+整理 ...

  9. 浅析WebRtc中视频数据的接收和渲染流程

    前言 本文基于PineAppRtc开源项目https://github.com/thfhongfeng/PineAppRtc 因为一个需求,我们需要将WebRtc发送过来的视频流中转出去,所以就研究一 ...

  10. uni 修改数据页面不重新渲染

    uni 修改数据页面不重新渲染 由于数据层次太多,没有触发render函数进行自动更新,需手动调用,调用方式如下: this.$forceUpdate();

最新文章

  1. 成为真正的变革型CIO,代价几何?
  2. 推荐3款 Docker 认证的实用免费插件,帮助您快速构建云原生应用程序!
  3. CSAPP:Attack lab
  4. mongodb 存储过程 遍历表数据_mongodb查看数据库和表的信息
  5. NeurlPS 2019丨微软亚洲研究院 5 篇精选论文解读
  6. 史上最全近百条Oracle DBA日常维护SQL脚本指令
  7. 各种渲染软件设计理论详解
  8. 与计算机运算速度相关参数,计算机CPU运算速度是多少
  9. python 特殊方法实例
  10. ElasticSearch索引模板(template)操作:创建、查询、修改、删除
  11. java 表格_Java创建表格实例详解
  12. 多线程模拟火车站售票
  13. 爬虫进阶:Scrapy 抓取 boss 直聘、拉勾心得经验
  14. 糗事百科网站服务器,糗事百科神仙道官网
  15. 直播礼品_礼品信息打印机
  16. libVLC 添加图片和文本水印
  17. 犀牛视频下载器丨钜惠惊喜不断
  18. linux系统怎么连接显示器,Ubuntu下外接显示器双屏显示的方法
  19. 数据结构 02-栈概念、Python 中使用列表 list 实现栈
  20. U8多辅助核算汇总表查询表条件

热门文章

  1. Wannafly挑战赛11
  2. 《软件需求十步走》阅读笔记6
  3. Linux root找回密码的方法
  4. 用ssh反向隧道访问内网机器
  5. [20150228]Delayed Block Cleanout 2.txt
  6. BZOJ5232[Lydsy2017省队十连测] 好题
  7. 计算机文献影印版,计算机网络 参考文献
  8. redis循环键_javascript – for循环从redis延迟获取项目
  9. stringbuilder寻找字符串位置可能存在多个 java_java面试题整理(一)
  10. php 数组 utf8,PHP数组编码gbk与utf8互相转换的两种方法实例分享