編輯:關於Android編程
前言
再次寫聊天的時候才發現,代碼積累是一件非常重要的事情,就如這篇博客的意圖其實就是代碼積累的目的,其實沒什麼難度,但是一件很瑣碎的事情真的也需要時間去完成和調試,所以,獲取你在寫一個功能的時候會覺得並沒有多難,但是如果可以最好把代碼整理/積累下來。
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
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
ViewPager這個小demo實現的是可以左右循環滑動圖片,下面帶索引,滑到最後一頁在往右滑動就要第一頁,第一頁往左滑動就到最後一頁,先上效果圖,用美女圖片是我一貫的作
RecyclerView是android-support-v7-21版本中新增的一個Widget,官方介紹RecyclerView 是 ListView 的升級版本,更加
ScrollView添加提示Arrow(箭頭) 在ScrollView的滑動功能中,需要給用戶提示,可以滑動,可以添加兩個箭頭。
Ctrl+T可以知道這個類的所有子類和父類。如果你每個頁面都有相同的布局,那麼你就可以在 style文件裡面定義一個相同的style。然後這樣就可以防止代碼的重復使用。&