Android輸入框添加emoje表情圖標的實現(xiàn)代碼
前言
再次寫聊天的時候才發(fā)現(xiàn),代碼積累是一件非常重要的事情,就如這篇博客的意圖其實就是代碼積累的目的,其實沒什么難度,但是一件很瑣碎的事情真的也需要時間去完成和調(diào)試,所以,獲取你在寫一個功能的時候會覺得并沒有多難,但是如果可以最好把代碼整理/積累下來。
demo描述

demo的功能其實就是仿照微信的 聊天 emoje 選擇,采用了 viewpager+gridView 的方案,不過有空我會補上 recyclerView 的方案,目前還是先把功能實現(xiàn)了再說。另外在 TextView 和 EditText 中添加 emoje ,可以看看這篇博客:Android中使用TextView及EditText來實現(xiàn)表情圖標的顯示及插入功能 ,這篇博客中介紹了兩種方法:
方法一:使用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 看起,看看需要哪些數(shù)據(jù):
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函數(shù),計算出我們需要的最小頁數(shù)
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));
}
大結構基本就是這樣了,接著就是小細節(jié)了,比如gridView的創(chuàng)建和展示:
@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。
大結構基本已經(jīng)OK了,接著就要看比較核心的部分,Emoji 的處理,在接收到Event事件時,調(diào)用了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[]{
"[微笑]",
"[撇嘴]",
"[色]",
"[發(fā)呆]",
"[得意]",
"[流淚]",
"[害羞]",
"[閉嘴]",
"[睡]",
"[大哭]",
"[尷尬]",
"[發(fā)怒]",
"[調(diào)皮]",
"[呲牙]",
"[驚訝]",
"[難過]",
"[酷]",
"[冷汗]",
"[抓狂]",
"[吐]",
"[偷笑]",
"[愉快]",
"[白眼]",
"[傲慢]",
"[饑餓]",
"[困]",
"[驚恐]",
"[流汗]",
"[憨笑]",
"[悠閑]",
"[奮斗]",
"[咒罵]",
"[疑問]",
"[噓]",
"[暈]",
"[瘋了]",
"[衰]",
"[骷髏]",
"[敲打]",
"[再見]",
"[擦汗]",
"[摳鼻]",
"[鼓掌]",
"[糗大了]",
"[壞笑]",
"[左哼哼]",
"[右哼哼]",
"[哈欠]",
"[鄙視]",
"[委屈]",
"[快哭了]",
"[陰險]",
"[親親]",
"[嚇]",
"[可憐]",
"[菜刀]",
"[西瓜]",
"[啤酒]",
"[籃球]",
"[乒乓]",
"[咖啡]",
"[飯]",
"[豬頭]",
"[玫瑰]",
"[凋謝]",
"[嘴唇]",
"[愛心]",
"[心碎]",
"[蛋糕]",
"[閃電]",
"[炸彈]",
"[刀]",
"[足球]",
"[瓢蟲]",
"[便便]",
"[月亮]",
"[太陽]",
"[禮物]",
"[擁抱]",
"[強]",
"[弱]",
"[握手]",
"[勝利]",
"[抱拳]",
"[勾引]",
"[拳頭]",
"[差勁]",
"[愛你]",
"[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來異步操作。
基本就到這里了,回過來看寫的內(nèi)容,自己都懶得吐槽,不過,好在只要有具體的demo,能讀代碼,有沒有講解其實都還好,也不用怕自己之后看不懂了。
源碼下載:https://github.com/cjhandroid/EmojiInputDemo
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
淺談Android開發(fā)系列網(wǎng)絡篇之Retrofit
這篇文章主要介紹了淺談Android開發(fā)系列網(wǎng)絡篇之Retrofit,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12
clipse項目遷移到android studio的方法(圖文最新版)
這篇文章主要介紹了clipse項目遷移到android studio(圖文最新版),需要的朋友可以參考下2015-10-10
Android中三種onClick的實現(xiàn)方式與對比
這篇文章主要為大家詳細介紹了Android中三種onClick的實現(xiàn)方式以及詳細對比,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2025-04-04
Android中HttpURLConnection類使用介紹
早些時候其實我們都習慣性使用HttpClient,但是后來Android6.0之后不再支持HttpClient,需要添加Apache的jar才行,所以,就有很多開發(fā)者放棄使用HttpClient了,HttpURLConnection畢竟是標準Java接口(java.net) ,適配性還是很強的2022-12-12

