前言

再次写聊天的时候才发现,代码积累是一件非常重要的事情,就如这篇博客的意图其实就是代码积累的目的,其实没什么难度,但是一件很琐碎的事情真的也需要时间去完成和调试,所以,获取你在写一个功能的时候会觉得并没有多难,但是如果可以最好把代码整理/积累下来。


demo描述

demo的功能其实就是仿照微信的 聊天 emoje 选择,采用了 viewpager+gridView 的方案,不过有空我会补上 recyclerView 的方案,目前还是先把功能实现了再说。另外在 TextView 和 EditText 中添加 emoje ,可以看看这篇博客:Android中使用TextView及EditText来实现表情图标的显示及插入功能 ,这篇博客中介绍了两种方法: 方法一:使用Html.fromHtml解析, 方法二:使用Bitmap直接画出来,我采用了第二种方法,使用bitmap画出来。


Read the fucking code

思路:既然是 viewpager + gridview 那么,先从大方向入手,完成 viewpager,再去完成 gridview。PS:代码里面使用了 RxJava、lambda、ButterKnife、EventBus、Glide。

这里将整个底部布局写成了一个组合的ViewGroup – ChatBottomBar,先从布局开始。

ChatBottomBar 的 XML – chat_bottom.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:animateLayoutChanges="true"android:orientation="vertical"><include layout="@layout/chat_bottom_input"></include><include layout="@layout/chat_bottom_function1"></include></LinearLayout>

以下分别是 输入框的 xml 和 Emoji 的 xml:

chat_bottom_input:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><RelativeLayout
        android:id="@+id/rl_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#f0f0f0"><ImageView
            android:id="@+id/showMore"android:layout_width="42dp"android:layout_height="60dp"android:paddingBottom="5dp"android:paddingLeft="9dp"android:paddingTop="9dp"android:src="@mipmap/ic_launcher" /><LinearLayout
            android:layout_width="match_parent"android:layout_height="35dp"android:layout_centerVertical="true"android:layout_marginRight="15dp"android:layout_toRightOf="@+id/showMore"android:background="@drawable/shape_white_corner"android:gravity="center_vertical"android:orientation="horizontal"><ImageView
                android:layout_width="45dp"android:layout_height="40dp"android:paddingBottom="10dp"android:paddingLeft="10dp"android:paddingRight="5dp"android:paddingTop="10dp"android:src="@mipmap/ic_launcher" /><EditText
                android:id="@+id/editText"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginRight="10dp"android:background="@null"android:gravity="center_vertical"android:hint="说点什么"android:maxLines="3"android:textColor="#999999"android:textColorHint="#dddddd"android:textSize="13sp" /></LinearLayout></RelativeLayout></merge>

chat_bottom_function1:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#ffffff"android:orientation="vertical"><android.support.v4.view.ViewPager
        android:id="@+id/emojes"android:layout_width="match_parent"android:layout_height="110dp"></android.support.v4.view.ViewPager></LinearLayout>

首先是 viewpager 填充 gridView,从 PageAdapter 看起,看看需要哪些数据:

package cjh.emojicondemo;import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.widget.GridView;import java.util.ArrayList;/*** Created by cjh on 16-11-8.*/public class EmojiPageAdapter extends PagerAdapter {private ArrayList<GridView> mLists;public EmojiPageAdapter(Context context, ArrayList<GridView> array) {this.mLists = array;}@Overridepublic int getCount() {return mLists.size();}@Overridepublic boolean isViewFromObject(View arg0, Object arg1) {return arg0 == arg1;}@Overridepublic Object instantiateItem(View arg0, int arg1) {((ViewPager) arg0).addView(mLists.get(arg1));return mLists.get(arg1);}@Overridepublic void destroyItem(View arg0, int arg1, Object arg2) {((ViewPager) arg0).removeView((View) arg2);}
}

其实基本就是PagerAdapter的模板代码,需要的仅仅只是 gridView,看下在ChatbottomBar中的代码:

@BindView(R.id.emojes)
android.support.v4.view.ViewPager emojes;
....
//每一页有24个表情,然后使用Math的ceil函数,计算出我们需要的最小页数private void initEmoje() {int pageCount = (int) Math.ceil(EmojiUtils.emojis.length / 24.0f);ArrayList<GridView> pageData = new ArrayList<>();for (int i = 0; i < pageCount; i++) {GridView gv = getGridView(i);pageData.add(gv);}emojes.setAdapter(new EmojiPageAdapter(context, pageData));}   

大结构基本就是这样了,接着就是小细节了,比如gridView的创建和展示:

  @NonNullprivate GridView getGridView(int i) {GridView gv = new GridView(context);gv.setVerticalScrollBarEnabled(false);gv.setAdapter(new EmojiGridAdapter(context, i));gv.setGravity(Gravity.CENTER);gv.setClickable(true);gv.setFocusable(true);gv.setNumColumns(8);return gv;}

adapter:

package cjh.emojicondemo;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;import org.greenrobot.eventbus.EventBus;/*** Created by cjh on 16-11-8.*/public class EmojiGridAdapter extends BaseAdapter {private Context context;private int page;public EmojiGridAdapter(Context context, int page) {this.context = context;this.page = page;}@Overridepublic int getCount() {return 24;}@Overridepublic Object getItem(int i) {return null;}@Overridepublic long getItemId(int i) {return 0;}@Overridepublic View getView(int i, View view, ViewGroup viewGroup) {ViewHolder holder = null;if (view == null) {view = LayoutInflater.from(context).inflate(R.layout.chat_emoji, null);holder = new ViewHolder();holder.image = (ImageView) view.findViewById(R.id.image);view.setTag(holder);}holder = (ViewHolder) view.getTag();int position = page * 23 + i;if (position < EmojiUtils.emojis.length)ImageLoader.load(context, EmojiUtils.icons[position], holder.image);elseholder.image.setVisibility(View.GONE);holder.image.setOnClickListener(view1 -> EventBus.getDefault().post(new EmojiEvent(EmojiUtils.emojis[page * 23 + i])));return view;}static class ViewHolder {public ImageView image;}
}

在这里,点击时间的传递我使用的是EventBus。

大结构基本已经OK了,接着就要看比较核心的部分,Emoji 的处理,在接收到Event事件时,调用了chatBottomBar.appandEmoje(emojiEvent.s)

@Subscribepublic void onEmojiEvent(EmojiEvent emojiEvent) {chatBottomBar.appandEmoje(emojiEvent.s);}

那么来看看ChatBottomBar的代码:

public void appandEmoje(String s) {rx.Observable.just(s).subscribeOn(Schedulers.io()).map(s1 -> {SpannableString emojeText = EmojiUtils.getEmojiText(editText.getText().toString() + s1);return emojeText;}).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(s2 -> {editText.setText("");editText.append(s2);});}

上面代码使用了RXJAVA,可以看到真正的核心是在
EmojiUtils.getEmojiText(editText.getText().toString() + s1);
return emojeText;
这行代码里面。

那么就来看看 EmojiUtils 的代码吧:

package cjh.emojicondemo;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.text.style.RelativeSizeSpan;
import android.util.SparseArray;import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.Inflater;/*** Created by cjh on 16-11-7.*/public class EmojiUtils {private static HashMap<Pattern, Integer> emoMap = new HashMap<>();public static final String DELETE_KEY = "em_delete_delete_expression";public static String[] emojis = new String[]{"[微笑]","[撇嘴]","[色]","[发呆]","[得意]","[流泪]","[害羞]","[闭嘴]","[睡]","[大哭]","[尴尬]","[发怒]","[调皮]","[呲牙]","[惊讶]","[难过]","[酷]","[冷汗]","[抓狂]","[吐]","[偷笑]","[愉快]","[白眼]","[傲慢]","[饥饿]","[困]","[惊恐]","[流汗]","[憨笑]","[悠闲]","[奋斗]","[咒骂]","[疑问]","[嘘]","[晕]","[疯了]","[衰]","[骷髅]","[敲打]","[再见]","[擦汗]","[抠鼻]","[鼓掌]","[糗大了]","[坏笑]","[左哼哼]","[右哼哼]","[哈欠]","[鄙视]","[委屈]","[快哭了]","[阴险]","[亲亲]","[吓]","[可怜]","[菜刀]","[西瓜]","[啤酒]","[篮球]","[乒乓]","[咖啡]","[饭]","[猪头]","[玫瑰]","[凋谢]","[嘴唇]","[爱心]","[心碎]","[蛋糕]","[闪电]","[炸弹]","[刀]","[足球]","[瓢虫]","[便便]","[月亮]","[太阳]","[礼物]","[拥抱]","[强]","[弱]","[握手]","[胜利]","[抱拳]","[勾引]","[拳头]","[差劲]","[爱你]","[NO]","[OK]"};public static int[] icons = new int[]{R.drawable.ee_1,R.drawable.ee_2,R.drawable.ee_3,R.drawable.ee_4,R.drawable.ee_5,R.drawable.ee_6,R.drawable.ee_7,R.drawable.ee_8,R.drawable.ee_9,R.drawable.ee_10,R.drawable.ee_11,R.drawable.ee_12,R.drawable.ee_13,R.drawable.ee_14,R.drawable.ee_15,R.drawable.ee_16,R.drawable.ee_17,R.drawable.ee_18,R.drawable.ee_19,R.drawable.ee_20,R.drawable.ee_21,R.drawable.ee_22,R.drawable.ee_23,R.drawable.ee_24,R.drawable.ee_25,R.drawable.ee_26,R.drawable.ee_27,R.drawable.ee_28,R.drawable.ee_29,R.drawable.ee_30,R.drawable.ee_31,R.drawable.ee_32,R.drawable.ee_33,R.drawable.ee_34,R.drawable.ee_35,R.drawable.ee_36,R.drawable.ee_37,R.drawable.ee_38,R.drawable.ee_39,R.drawable.ee_40,R.drawable.ee_41,R.drawable.ee_42,R.drawable.ee_43,R.drawable.ee_44,R.drawable.ee_45,R.drawable.ee_46,R.drawable.ee_47,R.drawable.ee_48,R.drawable.ee_49,R.drawable.ee_50,R.drawable.ee_51,R.drawable.ee_52,R.drawable.ee_53,R.drawable.ee_54,R.drawable.ee_55,R.drawable.ee_56,R.drawable.ee_57,R.drawable.ee_58,R.drawable.ee_59,R.drawable.ee_60,R.drawable.ee_61,R.drawable.ee_62,R.drawable.ee_63,R.drawable.ee_64,R.drawable.ee_65,R.drawable.ee_66,R.drawable.ee_67,R.drawable.ee_68,R.drawable.ee_69,R.drawable.ee_70,R.drawable.ee_71,R.drawable.ee_72,R.drawable.ee_73,R.drawable.ee_74,R.drawable.ee_75,R.drawable.ee_76,R.drawable.ee_77,R.drawable.ee_78,R.drawable.ee_79,R.drawable.ee_80,R.drawable.ee_81,R.drawable.ee_82,R.drawable.ee_83,R.drawable.ee_84,R.drawable.ee_85,R.drawable.ee_86,R.drawable.ee_87,R.drawable.ee_88,R.drawable.ee_89,R.drawable.ee_90,};static {for (int i = 0; i < emojis.length; i++) {emoMap.put(Pattern.compile(Pattern.quote(emojis[i])), icons[i]);}}public static SpannableString getEmojiText(String s) {SpannableString spannable = new SpannableString(s);for (Map.Entry<Pattern, Integer> entry : emoMap.entrySet()) {Matcher matcher = entry.getKey().matcher(spannable);while (matcher.find()) {for (ImageSpan span : spannable.getSpans(matcher.start(),matcher.end(), ImageSpan.class))if (spannable.getSpanStart(span) >= matcher.start()&& spannable.getSpanEnd(span) <= matcher.end())spannable.removeSpan(span);elsebreak;Drawable drawable = MainActivity.context.getResources().getDrawable(entry.getValue());drawable.setBounds(0, 0, 60, 60);ImageSpan imageSpan = new ImageSpan(drawable);spannable.setSpan(imageSpan,matcher.start(), matcher.end(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);}}return spannable;}
}

这里为了方便知道插入表情的位置,我将emoji对应的中文转化成了Pattern对象,在getEmojiText里面做了遍历查询比对,这也就是为什么我会使用RX来异步操作。


基本就到这里了,回过来看写的内容,自己都懒得吐槽,不过,好在只要有具体的demo,能读代码,有没有讲解其实都还好,也不用怕自己之后看不懂了。
源码下载

输入框添加Emoje表情demo相关推荐

  1. android添加文本框代码,Android输入框添加emoje表情图标的实现代码

    前言 再次写聊天的时候才发现,代码积累是一件非常重要的事情,就如这篇博客的意图其实就是代码积累的目的,其实没什么难度,但是一件很琐碎的事情真的也需要时间去完成和调试,所以,获取你在写一个功能的时候会觉 ...

  2. markdown中添加Emoji表情让文章更有趣味

    markdown表情 在文章中添加Emoji表情,可以添加文章的趣味性,Emoji使用规则是双::中间加单词 查看全文 http://www.taodudu.cc/news/show-4737612. ...

  3. 在EditText中添加QQ表情

    本文参考自:http://blog.csdn.net/wulianghuan/article/details/8583921 在输入框中输入表情是每个聊天软件的必备功能,做到这点仅需要将表情放入工程图 ...

  4. listView无需适配器添加数据(写demo快速开发)entries属性的特殊用法

    先看下简单的布局代码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:a ...

  5. html5输入框增加语音,为任意输入框添加语音输入功能

    最近大家是否也发现了,百度谷歌等好几个搜索引擎的输入框都有一个语音图标按钮了呢?其实这个功能的技术没有那么高深的,这个技术的实现其实就是HTML5里面的一个标签而已,,点击麦克风就能够进行语音识别了. ...

  6. 微信公众号自定义菜单添加emoji表情图标

    微信公众号自定义菜单添加emoji表情图标 第一步:打开微信公众平台接口调试工具,点击前往接口调试工具: 第二步:把这段代码   {"button":[{"sub_but ...

  7. HTML5为输入框添加语音输入功能的实现方法

    这里介绍的是大家以后要用到的html强大功能,可直接给输入框增加语音功能,下面我们先来看看实现方法. 大家可以看到在输入框右边的麦克风图标,点击麦克风就能够进行语音识别了. 其实很简单,语音识别是ht ...

  8. 怎么将迅捷思维导图添加小表情

    思维导图是一种将放射性思考具体化的方法.我们知道放射性思考是人类大脑的自然思考方式,每一种进入大脑的资料,不论是感觉.记忆或是想法--包括文字.数字.符码.食物.香气.线条.颜色.意象.节奏.音符等, ...

  9. html5 语音输入小话筒,科技常识:HTML5为输入框添加语音输入功能的实现方法

    今天小编跟大家讲解下有关HTML5为输入框添加语音输入功能的实现方法 ,相信小伙伴们对这个话题应该有所关注吧,小编也收集到了有关HTML5为输入框添加语音输入功能的实现方法 的相关资料,希望小伙伴们看 ...

最新文章

  1. Gym 101334A Area 51 数学
  2. 2021年广东赛区线上比赛高校组合点-五邑大学
  3. python 字符串形式的列表 转 列表
  4. springMVC实现文件下载(附带Servlet方式)
  5. Spring实战——缓存
  6. 升级浏览器_星愿浏览器升级至6.3.2000.2001
  7. Rust中Box、Rc、Arc、Cell、RefCell、Cow简介
  8. 寻找新冠“解药”:在 10^60 化合物分子空间,他们用 AI 挖掘潜在药物
  9. utilities(matlab)—— 合成数据(synthesis data)
  10. 在php定界符中,PHP中的定界符 - ho俊的个人空间 - OSCHINA - 中文开源技术交流社区...
  11. 栈的应用 算术表达式转换为后缀表达式
  12. Android之崩溃日志本地存储与远程保存
  13. 网络故障一例:网络不能用,ping出错
  14. 顺序不能改变的算子,是否跟时间有关
  15. [git]git 分支
  16. 扫雷游戏网页版_梦幻西游出网页版,王者出新英雄阿古朵,谁在杀死国产游戏的创新...
  17. PHP实现微信网页登陆授权开发
  18. 微信小程序新版本提示更新
  19. 机器学习笔记之狄利克雷过程(五)——基于狄利克雷过程的预测任务
  20. 使用 LDAP Browser 登入ad域控提示 Root error: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C0903C5, commen

热门文章

  1. App Store 审核指南 2017-12-13
  2. Cuda相关的函数数
  3. 【软件推荐】Linux的一些好玩的软件
  4. 学习html的心得体会
  5. c语言处理nc程序,NC程序的语言问题
  6. 不是你不懂黑盒攻击,而是你还不懂SurFree
  7. Age Estimation
  8. MySQL 一键安装脚本
  9. 预见2020下半场——从自动驾驶新趋势看普及前景
  10. php找爸爸,暖哭!萌娃外滩找爸爸:“他两天没回家”