Android高仿iOS Messages聊天气泡
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聊天气泡相关推荐
- Android高仿iOS Messages录音操作按钮
Android高仿iOS Messages录音操作按钮 目录 一.目标 二.功能分析 三.实现效果 四.实现过程 五.开发过程回顾 六.接下来 七.Finally 前面的2次开发,分别完成了实现录音和 ...
- android高仿微信聊天页面,Android 高仿微信语音聊天页面高斯模糊(毛玻璃效果)
目前的应用市场上,使用毛玻璃效果的APP随处可见,比如用过微信语音聊天的人可以发现,语音聊天页面就使用了高斯模糊效果. 先看下效果图: 仔细观察上图,我们可以发现,背景图以用户头像为模板,对其进行了高 ...
- android高仿ios控制中心,高仿ios控制中心安卓版
高仿ios控制中心安卓版是一款非常好用的安卓仿苹果手机控制中心的软件,能够让使用安卓手机的朋友们随时体验苹果手机的系统,操作简单方便,软件也是非常稳定的,大家可放心的下载使用,感兴趣的用户们就前来下载 ...
- android 高仿ios开关,Android自定义view仿IOS开关效果
本文主要讲解如何在 Android 下实现高仿 iOS 的开关按钮,并非是在 Android 自带的 ToggleButton 上修改,而是使用 API 提供的 onDraw.onMeasure.Ca ...
- Android 高仿微信实时聊天 基于百度云推送
一直在仿微信界面,今天终于有幸利用百度云推送仿一仿微信聊天了~~~ 首先特别感谢:weidi1989分享的Android之基于百度云推送IM ,大家可以直接下载:省了很多事哈,本例中也使用了weidi ...
- Android高仿IOS和QQ的弹出对话框
我们知道Android中其实并不提供圆形的东西,像Button,TextView,EditView等等都是没有弧形元素在里面(看看这些控件的属性就知道了).而很多时候我们的程序中又需要用到这样有弧形元 ...
- android 高仿ios时间选择器,仿ios时间选择
再mui得picker的基础上修改为类似ios选择时间的插件. muipicker exapmple地址 把里面数据换成下面的数据就可以了. (function($, doc) { $.init(); ...
- android 高仿ios水滴,iOS仿芝麻信用水滴效果
开篇: 最近接到客户需要开发(就按某付宝那个页面给我做一个)的新需求,大体就是类似芝麻信用的水滴效果(图1)用来展示用户的信用积分.苦于没有思路,找到的iOS实现的文章基本都是类似图2仪表盘的实现思路 ...
- android仿微信聊天功能,Android高仿微信聊天界面代码分享
微信聊天现在非常火,是因其界面漂亮吗,哈哈,也许吧.微信每条消息都带有一个气泡,非常迷人,看起来感觉实现起来非常难,其实并不难.下面小编给大家分享实现代码. 先给大家展示下实现效果图: OK,下面我们 ...
最新文章
- python画心形图像
- 学JS的心路历程 -物件与原型(二)
- python3 问题 No module named _sqlite3 解决方案
- 查询数据库中的表结构信息
- 上海计算机应用基础自考上机,上海2010年自考计算机应用基础上机大纲
- Java NIO示例:多人网络聊天室
- IOS开发报错之Undefined symbols for architecture armv6
- usb PHY linux驱动
- [Selenium] 最大化或自定义浏览器的大小
- java数据类型_JAVA基础篇(数据类型)
- Find Backpacker Jobs in Australia
- Meta标签中的viewport属性及含义
- 界面怎么随意设计java,怎么样设计一个图形用户界面(普通登陆框)用java语言中的swing...
- hive中:row_number()排序函数的使用
- 斩不断、理还乱,论家事,别有一番滋味在心头。
- 手机产业未来的发展方向
- FineReport 填报报表数据自定义提交
- 程序员笔试之猿辅导招聘2021 编程题收集整理(八)
- 传奇世界私服务器端制作,关于内网架设传奇世界私服问题的一些解答
- Android连接夜神模拟器详细步骤