Android输入框添加emoje表情图标的实现代码
前言
再次写聊天的时候才发现,代码积累是一件非常重要的事情,就如这篇博客的意图其实就是代码积累的目的,其实没什么难度,但是一件很琐碎的事情真的也需要时间去完成和调试,所以,获取你在写一个功能的时候会觉得并没有多难,但是如果可以最好把代码整理/积累下来。
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; } @override public int getcount() { return mlists.size(); } @override public boolean isviewfromobject(view arg0, object arg1) { return arg0 == arg1; } @override public object instantiateitem(view arg0, int arg1) { ((viewpager) arg0).addview(mlists.get(arg1)); return mlists.get(arg1); } @override public 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的创建和展示:
@nonnull private 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; } @override public int getcount() { return 24; } @override public object getitem(int i) { return null; } @override public long getitemid(int i) { return 0; } @override public 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); else holder.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)
@subscribe public 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); else break; 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,能读代码,有没有讲解其实都还好,也不用怕自己之后看不懂了。
源码下载:https://github.com/cjhandroid/emojiinputdemo
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Android自定义实现开关按钮代码
下一篇: ASP+CSS 实现列表隔行换色