星期六日閒來無事,看到之前寫的一個聊天IM程序,在這個程序的聊天界面有一個自定義的輸入法控件,效果還是挺不錯,可以隨意切換表情、語音輸入、軟鍵盤文本輸入和其他功能選擇,類似於微信控件。表情框架使用了LQREmojiLibrary表情庫,個人感覺這個東西就像作者說的一樣,超級牛逼,我把源碼拷貝了下來,單獨使用了library表情庫,這樣我就可以隨意往裏面添加自己想要的表情了;真的十分方便,順便貼上該作者的該表情庫的github地址:https://github.com/GitLqr/LQREmojiLibrary;確實挺牛逼
好了,言歸正傳,我將輸入控件單獨扣出來做成了一個程序,自定義控件也在裏面,歡迎大家上github:https://github.com/TenXu/chat_input,歡迎大家fork一下我。
以下是該程序的運行效果:
界面初始效果
輸入文字發送成功,在具體的項目當中需要做聊天會話列表的顯示
選擇表情,輸入表情
選擇錄音,項目需要有錄音權限和讀取內存權限
錄音過程
取消錄音
功能塊,具體項目具體實現
大致說一下ChatInput控件的佈局:
<?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="@color/gray4" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="1px" android:focusable="true" android:focusableInTouchMode="true" android:background="@color/line" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="center_vertical" android:orientation="horizontal"> <ImageView android:id="@+id/id_voice" android:layout_width="35dp" android:layout_height="35dp" android:layout_margin="5dp" android:src="@drawable/ic_voice_input" /> <RelativeLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center"> <EditText android:id="@+id/id_input_content" android:layout_width="match_parent" android:layout_height="40dp" android:background="@null" android:drawableBottom="@drawable/selector_edit_focus" android:visibility="visible" /> <tenxu.chat_input.view.AudioRecordButton android:id="@+id/id_to_speak" style="?android:attr/borderlessButtonStyle" android:layout_width="match_parent" android:layout_height="40dp" android:background="@drawable/button_recorder_normal" android:minHeight="0dp" android:padding="5dp" android:text="@string/str_recorder_normal" android:textColor="@color/gray1" android:visibility="gone" /> </RelativeLayout> <ImageView android:id="@+id/id_emo" android:layout_width="35dp" android:layout_height="35dp" android:layout_margin="5dp" android:src="@drawable/ic_face_input" /> <ImageView android:id="@+id/id_add" android:layout_width="35dp" android:layout_height="35dp" android:layout_marginLeft="5dp" android:layout_marginRight="7dp" android:src="@drawable/ic_add_input" /> <Button android:id="@+id/id_send" style="?android:attr/borderlessButtonStyle" android:layout_width="43dp" android:layout_height="35dp" android:layout_marginRight="4dp" android:background="@drawable/btn_send" android:textColor="@color/white" android:textSize="13sp" android:visibility="gone" /> </LinearLayout> <FrameLayout android:id="@+id/id_bottom_fl" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone"> <!--表情貼圖區--> <com.lqr.emoji.EmotionLayout android:id="@+id/id_epv" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /> <!--按鈕功能區域--> <LinearLayout android:id="@+id/id_ll_buttom_func" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:visibility="gone"> <View style="@style/Line1" /> <android.support.v4.view.ViewPager android:id="@+id/id_vp_func" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout> </FrameLayout> </LinearLayout>佈局還是相對挺簡單的,就是相應的輸入框和按鈕圖片等等,而按鈕功能區域使用ViewPager是爲了可以方便的添加更多的功能,像微信聊天界面一樣,可以選擇多種功能,左右滑動選擇不同頁面功能等;而ChatInput控件主要就是對切換操作的邏輯進行處理,主要邏輯爲:
/** * 需要在activity方法的initView中調用 * 初始化表情內容 */ public void initEmotionKeyboard(Activity activity, ListView messageList, final ScrollBottomListener listener) { //1、創建EmotionKeyboard對象 EmotionKeyboard mEmotionKeyboard = EmotionKeyboard.with(activity); //2、綁定輸入框控件 mEmotionKeyboard.bindToEditText(mIdInputContent); //3、綁定輸入框上面的消息列表控件 mEmotionKeyboard.bindToContent(messageList); //4、綁定輸入框下面的底部區域(這裏是把表情區和功能區共放在FrameLayout下,所以綁定的控件是FrameLayout) mEmotionKeyboard.setEmotionLayout(mIdBottomFl); //5、綁定表情按鈕(可以綁定多個,如微信就有2個,一個是表情按鈕,一個是功能按鈕) mEmotionKeyboard.bindToEmotionButton(mIdEmo, mIdAdd); //6、當在第5步中綁定了多個EmotionButton時,這裏的回調監聽的view就有用了,注意是爲了判斷是否要自己來控制底部的顯隱,還是交給EmotionKeyboard控制 mEmotionKeyboard.setOnEmotionButtonOnClickListener(new EmotionKeyboard.OnEmotionButtonOnClickListener() { @Override public boolean onEmotionButtonOnClickListener(View view) { if (mIdToSpeak.getVisibility() == View.VISIBLE) { hideSpeak(); mIdVoice.setImageResource(R.drawable.ic_voice_input); } //輸入框底部顯示時 if (mIdBottomFl.getVisibility() == View.VISIBLE) { //表情控件顯示而點擊的按鈕是ivAdd時,攔截事件,隱藏表情控件,顯示功能區 if (mIdEpv.getVisibility() == View.VISIBLE && view.getId() == R.id.id_add) { mIdEpv.setVisibility(View.GONE); mIdLlButtomFunc.setVisibility(View.VISIBLE); return true; //功能區顯示而點擊的按鈕是ivEmo時,攔截事件,隱藏功能區,顯示錶情控件 } else if (mIdLlButtomFunc.getVisibility() == View.VISIBLE && view.getId() == R.id.id_emo) { mIdEpv.setVisibility(View.VISIBLE); mIdLlButtomFunc.setVisibility(View.GONE); return true; } else { openKeyBoardAndGetFocus(); mIdBottomFl.setVisibility(View.GONE); //滾動到最後 listener.scrollBottom(); return true; } } else { if (view.getId() == R.id.id_emo) { mIdEpv.setVisibility(View.VISIBLE); mIdLlButtomFunc.setVisibility(View.GONE); //點擊id_add,顯示功能區 } else { mIdEpv.setVisibility(View.GONE); mIdLlButtomFunc.setVisibility(View.VISIBLE); } } //滾動到最後 listener.scrollBottom(); return false; } }); }這裏就是主要的切換邏輯了,也並不複雜,不過確實ChatInput的核心吧。
底部功能區域也需要初始化,既然用了ViewPager控件, 就需要用Fragment來填充:
/** * 需要在activity方法的initView中調用 * 初始化底部功能區 */ public Func1Fragment initBottomFunc(FragmentManager manager, Func1Fragment.PrepareFunc prepareFunc) { //底部功能區 List<BaseFragment> fragments = new ArrayList<>(); Func1Fragment func1Fragment1 = Func1Fragment.getInstance(this,prepareFunc,mIdBottomFl,mIdLlButtomFunc,mIdEpv); fragments.add(func1Fragment1); BottomFuncAdapter bottomFucAdapter = new BottomFuncAdapter(manager, fragments); mIdVpFunc.setAdapter(bottomFucAdapter); return func1Fragment1; }咱們再來看一下聊天界面的佈局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/id_content_lv" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <tenxu.chat_input.view.ChatInput android:id="@+id/input_panel" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
也就是一個ListView和自定義的ChatInput,搞定!
那麼ChatInput有些東西需要在Activity當中初始化的,如下:
mInput.initEmotionKeyboard(this, mIdContentLv, new ChatInput.ScrollBottomListener() { @Override public void scrollBottom() { } }); Func1Fragment fragment = mInput.initBottomFunc(getSupportFragmentManager(), new Func1Fragment.PrepareFunc() { @Override public void prepareFunc1(ChatInput chatInput) { // if (chatInput.requestCamera(MainActivity.this) && chatInput.requestStorage(MainActivity.this)) { // Intent intent = new Intent(MainActivity.this, ImageGridActivity.class); // startActivityForResult(intent, IMAGE_PICKER); // } } @Override public void prepareFunc2(ChatInput chatInput) { // Intent intent = new Intent(Intent.ACTION_GET_CONTENT); // intent.setType("*/*"); // startActivityForResult(intent, FILE_CODE); } @Override public void prepareFunc3(ChatInput chatInput) { } @Override public void prepareFunc4(ChatInput chatInput) { } }); fragment.initFuncListener(); mInput.initAudioFinishRecorder(new AudioRecordButton.AudioFinishRecorderListener() { @Override public void onFinish(float seconds, String filePath) { // uploadFileToServer(seconds, filePath, Constants.VOICE_TYPE, null); } }); mInput.setmActivity(this);
initEmotionKeyboard方法的回調參數是爲了點擊表情按鈕,讓聊天內容滾動最底部的功能;當然,也可以做其他的,那麼就由使用者自己去搞了。
initBottomFunc方法是初始化底部功能,並且回調出來有多個函數,都是點擊功能所需要做的事情,實現由使用者自己實現
initAudioFinishRecorder方法回調出來的是一個seconds和filePath,看字面意思也很清楚,一個是事件,一個是文件地址,那麼就是錄音的時長和錄音文件的地址了,之後要幹嘛,也是由使用者自己定義了。
大概的就講這麼一些吧,大家可以下載源碼看看,如果有任何建議都歡迎大家提出來。
最後貼一下源碼地址:https://github.com/TenXu/chat_input