Android高仿iOS Messages聊天气泡

  • 一、目标
  • 二、功能分析
  • 三、实现代码
    • 1. ChatItem
    • 2. DateItem
    • 3. TextItem
    • 4. PhotoItem
    • 5. ChatViewHolder
  • 四、开发过程回顾
  • 五、接下来
  • 六、Finally

在《iOS Messages显示图片功能分析》一文中,我们总结了iOS Messages的气泡形状。现在着手实现之。

一、目标

实现iOS Messages聊天气泡。

二、功能分析

神马笔记当前只能发送信息,不会收到信息,因此只考虑发送消息的气泡形状。

除了没有气泡外,共有6种气泡形状。

考虑消息时间的连续性,有4种——单独、开始、中间、结束,使用4种气泡形状已经足够。

同时考虑消息类型的连续型,同样有4种——单独、开始、中间、结束。

二者存在重叠情况,最终16种情况合并为6种气泡形状。

  • 气泡形状

尖角气泡总是出现在对话告一段落的情况。

定义 描述
BUBBLE_NONE 没有气泡,如日期
BUBBLE_SINGLE 单个气泡
BUBBLE_SINGLE_TAIL 单个尖角气泡
BUBBLE_START 开始
BUBBLE_MIDDLE 中间
BUBBLE_END 结束气泡
BUBBLE_END_TAIL 结束尖角气泡
  • 时间类型
定义 描述
STYLE_SINGLE 单独时间点
STYLE_START 开始时间点
STYLE_MIDDLE 中间时间点
STYLE_END 结束时间点
  • 消息类型
定义 描述
STYLE_SINGLE 单独类型
STYLE_START 开始
STYLE_MIDDLE 中间
STYLE_END 结束
  • 关系表
时间类型 消息类型 气泡类型
STYLE_SINGLE
STYLE_SINGLE BUBBLE_SINGLE_TAIL
STYLE_START BUBBLE_SINGLE_TAIL
STYLE_MIDDLE BUBBLE_SINGLE_TAIL
STYLE_END BUBBLE_SINGLE_TAIL
STYLE_START
STYLE_SINGLE BUBBLE_SINGLE
STYLE_START BUBBLE_START
STYLE_MIDDLE BUBBLE_START
STYLE_END BUBBLE_SINGLE
STYLE_MIDDLE
STYLE_SINGLE BUBBLE_SINGLE
STYLE_START BUBBLE_START
STYLE_MIDDLE BUBBLE_MIDDLE
STYLE_END BUBBLE_END
STYLE_END
STYLE_SINGLE BUBBLE_SINGLE_TAIL
STYLE_START BUBBLE_SINGLE_TAIL
STYLE_MIDDLE BUBBLE_END_TAIL
STYLE_END BUBBLE_END_TAIL

三、实现代码

1. ChatItem

方法 描述 依赖
public int getBubble() 获取气泡形状 getTimeStyle()
getTypeStyle()
protected int getTimeStyle() 获取时间类型 isTimeContinuous()
protected int getTypeStyle() 获取消息类型 isTypeContinuous()
protected boolean isTimeContinuous(ChatItem item) 判断时间是否连续,当前设定为3分钟。
protected boolean isTypeContinuous(ChatItem item) 判断类型是否连续
public class ChatItem<E extends MessageEntity> {public static final int TYPE_NONE   = 0;public static final int TYPE_DATE   = 1;public static final int TYPE_TEXT   = 2;public static final int TYPE_PHOTO  = 3;public static final int BUBBLE_NONE         = 0;public static final int BUBBLE_SINGLE       = 1;public static final int BUBBLE_SINGLE_TAIL  = 2;public static final int BUBBLE_START        = 3;public static final int BUBBLE_MIDDLE       = 4;public static final int BUBBLE_END          = 5;public static final int BUBBLE_END_TAIL     = 6;public static final int STYLE_SINGLE    = 0;public static final int STYLE_START     = 1;public static final int STYLE_MIDDLE    = 2;public static final int STYLE_END       = 3;int type;E entity;protected ChatProvider parent;public ChatItem(ChatProvider parent, E entity, int type) {this.parent = parent;this.entity = entity;this.type = type;}public E getEntity() {return entity;}public int getType() {return this.type;}public DateTime getCreated() {return entity.getCreated();}public int getBubble() {int bubble = BUBBLE_SINGLE_TAIL;int time = getTimeStyle();int type = getTypeStyle();switch (time) {case STYLE_SINGLE: {bubble = BUBBLE_SINGLE_TAIL;break;}case STYLE_START: {if (type == STYLE_SINGLE) {bubble = BUBBLE_SINGLE;} else if (type == STYLE_START) {bubble = BUBBLE_START;} else if (type == STYLE_MIDDLE) {bubble = BUBBLE_START;} else if (type == STYLE_END) {bubble = BUBBLE_SINGLE;}break;}case STYLE_MIDDLE: {if (type == STYLE_SINGLE) {bubble = BUBBLE_SINGLE;} else if (type == STYLE_START) {bubble = BUBBLE_START;} else if (type == STYLE_MIDDLE) {bubble = BUBBLE_MIDDLE;} else if (type == STYLE_END) {bubble = BUBBLE_END;}break;}case STYLE_END: {if (type == STYLE_SINGLE) {bubble = BUBBLE_SINGLE_TAIL;} else if (type == STYLE_START) {bubble = BUBBLE_SINGLE_TAIL;} else if (type == STYLE_MIDDLE) {bubble = BUBBLE_END_TAIL;} else if (type == STYLE_END) {bubble = BUBBLE_END_TAIL;}break;}}return bubble;}protected int getTimeStyle() {boolean pre = false;boolean next = false;int position = parent.indexOf(this);if (position > 0) {pre = isTimeContinuous(parent.get(position - 1));}if (position >= 0 && position < (parent.size() - 1)) {next = isTimeContinuous(parent.get(position + 1));}int style = STYLE_SINGLE;if (pre && next) {style = STYLE_MIDDLE;} else if (pre && !next) {style = STYLE_END;} else if (!pre && next) {style = STYLE_START;} else if (!pre && !next) {style = STYLE_SINGLE;}return style;}protected int getTypeStyle() {boolean pre = false;boolean next = false;int position = parent.indexOf(this);if (position > 0) {pre = isTypeContinuous(parent.get(position - 1));}if (position >= 0 && position < (parent.size() - 1)) {next = isTypeContinuous(parent.get(position + 1));}int style = STYLE_SINGLE;if (pre && next) {style = STYLE_MIDDLE;} else if (pre && !next) {style = STYLE_END;} else if (!pre && next) {style = STYLE_START;} else if (!pre && !next) {style = STYLE_SINGLE;}return style;}protected boolean isTypeContinuous(ChatItem item) {return this.getType() == item.getType();}protected boolean isTimeContinuous(ChatItem item) {return isTimeContinuous(this, item, 3);}static boolean isTimeContinuous(ChatItem current, ChatItem next, int minutes) {DateTime date = current.getCreated();DateTime now = next.getCreated();int diff = Minutes.minutesBetween(date, now).getMinutes();diff = Math.abs(diff);boolean result = (diff < minutes);return result;}
}

2. DateItem

没有气泡。

public class DateItem extends ChatItem<MessageEntity> {public DateItem(ChatProvider parent, MessageEntity entity) {super(parent, entity, TYPE_DATE);}@Overridepublic int getBubble() {return BUBBLE_NONE;}
}

3. TextItem

重载getTypeStyle方法,文本消息类型总看作单独的。

public class TextItem extends ChatItem<TextEntity> {public TextItem(ChatProvider parent, TextEntity entity) {super(parent, entity, TYPE_TEXT);}@Overrideprotected int getTypeStyle() {return STYLE_SINGLE;}public String getText() {return entity.getText();}}

4. PhotoItem

未来在判断类型连续性上会进行扩展,重载之。

public class PhotoItem extends ChatItem<PhotoEntity> {public PhotoItem(ChatProvider parent, PhotoEntity entity) {super(parent, entity, TYPE_PHOTO);}@Overrideprotected boolean isTypeContinuous(ChatItem item) {return super.isTypeContinuous(item);}public int getWidth() {return entity.getWidth();}public int getHeight() {return entity.getHeight();}public Uri getUri() {return entity.getUri();}public File getFile() {return entity.getFile();}public String getSignature() {return entity.getSignature();}
}

5. ChatViewHolder

根据气泡形状,获取对应的图片资源。

public abstract class ChatViewHolder<E extends ChatItem> extends BridgeViewHolder<E> {ViewStub viewStub;@Keeppublic ChatViewHolder(View itemView) {super(itemView);}@Overridepublic void onViewCreated(@NonNull View view) {this.viewStub = view.findViewById(R.id.stub);}public int getBubble(ChatItem item) {int resId = R.drawable.ic_outgoing_bubble_single_tail;switch (item.getBubble()) {case ChatItem.BUBBLE_NONE: {resId = 0;break;}case ChatItem.BUBBLE_SINGLE: {resId = R.drawable.ic_outgoing_bubble_single;break;}case ChatItem.BUBBLE_SINGLE_TAIL: {resId = R.drawable.ic_outgoing_bubble_single_tail;break;}case ChatItem.BUBBLE_START: {resId = R.drawable.ic_outgoing_bubble_start;break;}case ChatItem.BUBBLE_MIDDLE: {resId = R.drawable.ic_outgoing_bubble_middle;break;}case ChatItem.BUBBLE_END: {resId = R.drawable.ic_outgoing_bubble_end;break;}case ChatItem.BUBBLE_END_TAIL: {resId = R.drawable.ic_outgoing_bubble_end_tail;break;}}return resId;}
}

四、开发过程回顾

从6种气泡形状开始,发现决定气泡形状的2个参数——时间类型、消息类型。

根据时间类型和消息类型组合出对应管理。

再根据时间和类型的连续性计算出对应的类型。

从而最终计算出每条消息对应的气泡形状。

五、接下来

组合所有功能,实现神马笔记在对话中插入图片消息。

六、Finally

~若是经典所在之处~即为有佛~若尊重弟子~

Android高仿iOS Messages聊天气泡相关推荐

  1. Android高仿iOS Messages录音操作按钮

    Android高仿iOS Messages录音操作按钮 目录 一.目标 二.功能分析 三.实现效果 四.实现过程 五.开发过程回顾 六.接下来 七.Finally 前面的2次开发,分别完成了实现录音和 ...

  2. android高仿微信聊天页面,Android 高仿微信语音聊天页面高斯模糊(毛玻璃效果)

    目前的应用市场上,使用毛玻璃效果的APP随处可见,比如用过微信语音聊天的人可以发现,语音聊天页面就使用了高斯模糊效果. 先看下效果图: 仔细观察上图,我们可以发现,背景图以用户头像为模板,对其进行了高 ...

  3. android高仿ios控制中心,高仿ios控制中心安卓版

    高仿ios控制中心安卓版是一款非常好用的安卓仿苹果手机控制中心的软件,能够让使用安卓手机的朋友们随时体验苹果手机的系统,操作简单方便,软件也是非常稳定的,大家可放心的下载使用,感兴趣的用户们就前来下载 ...

  4. android 高仿ios开关,Android自定义view仿IOS开关效果

    本文主要讲解如何在 Android 下实现高仿 iOS 的开关按钮,并非是在 Android 自带的 ToggleButton 上修改,而是使用 API 提供的 onDraw.onMeasure.Ca ...

  5. Android 高仿微信实时聊天 基于百度云推送

    一直在仿微信界面,今天终于有幸利用百度云推送仿一仿微信聊天了~~~ 首先特别感谢:weidi1989分享的Android之基于百度云推送IM ,大家可以直接下载:省了很多事哈,本例中也使用了weidi ...

  6. Android高仿IOS和QQ的弹出对话框

    我们知道Android中其实并不提供圆形的东西,像Button,TextView,EditView等等都是没有弧形元素在里面(看看这些控件的属性就知道了).而很多时候我们的程序中又需要用到这样有弧形元 ...

  7. android 高仿ios时间选择器,仿ios时间选择

    再mui得picker的基础上修改为类似ios选择时间的插件. muipicker exapmple地址 把里面数据换成下面的数据就可以了. (function($, doc) { $.init(); ...

  8. android 高仿ios水滴,iOS仿芝麻信用水滴效果

    开篇: 最近接到客户需要开发(就按某付宝那个页面给我做一个)的新需求,大体就是类似芝麻信用的水滴效果(图1)用来展示用户的信用积分.苦于没有思路,找到的iOS实现的文章基本都是类似图2仪表盘的实现思路 ...

  9. android仿微信聊天功能,Android高仿微信聊天界面代码分享

    微信聊天现在非常火,是因其界面漂亮吗,哈哈,也许吧.微信每条消息都带有一个气泡,非常迷人,看起来感觉实现起来非常难,其实并不难.下面小编给大家分享实现代码. 先给大家展示下实现效果图: OK,下面我们 ...

最新文章

  1. python画心形图像
  2. 学JS的心路历程 -物件与原型(二)
  3. python3 问题 No module named _sqlite3 解决方案
  4. 查询数据库中的表结构信息
  5. 上海计算机应用基础自考上机,上海2010年自考计算机应用基础上机大纲
  6. Java NIO示例:多人网络聊天室
  7. IOS开发报错之Undefined symbols for architecture armv6
  8. usb PHY linux驱动
  9. [Selenium] 最大化或自定义浏览器的大小
  10. java数据类型_JAVA基础篇(数据类型)
  11. Find Backpacker Jobs in Australia
  12. Meta标签中的viewport属性及含义
  13. 界面怎么随意设计java,怎么样设计一个图形用户界面(普通登陆框)用java语言中的swing...
  14. hive中:row_number()排序函数的使用
  15. 斩不断、理还乱,论家事,别有一番滋味在心头。
  16. 手机产业未来的发展方向
  17. FineReport 填报报表数据自定义提交
  18. 程序员笔试之猿辅导招聘2021 编程题收集整理(八)
  19. 传奇世界私服务器端制作,关于内网架设传奇世界私服问题的一些解答
  20. Android连接夜神模拟器详细步骤

热门文章

  1. 初学JAVA 望大家给点帮助
  2. matlab function_MATLAB处理INCA采集数据(mdf,dat等)一
  3. 拉丁超立方体抽样(LHS)研究(Matlab代码实现)
  4. 为什么我一直在坚持着持续进步?
  5. 后缀为labels的文件_txt标注文档转换为labelmg VOC格式的xml标注文件
  6. 以首字母开头的所有汉字组合
  7. 【华为云计算产品系列】云上容灾架构实战部署详解
  8. miniPCIe接口CAN卡在工控机中的应用
  9. 调试器(二) cmsis-dap
  10. 编译原理-实验四-LR(0)语法分析程序的设计