Welcome

首页 / 移动开发 / Android / 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"><RelativeLayoutandroid:id="@+id/rl_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#f0f0f0"><ImageViewandroid: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" /><LinearLayoutandroid: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"><ImageViewandroid:layout_width="45dp"android:layout_height="40dp"android:paddingBottom="10dp"android:paddingLeft="10dp"android:paddingRight="5dp"android:paddingTop="10dp"android:src="@mipmap/ic_launcher" /><EditTextandroid: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.ViewPagerandroid: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,能读代码,有没有讲解其实都还好,也不用怕自己之后看不懂了。
源码下载:https://github.com/cjhandroid/EmojiInputDemo
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。