聊天工具輸入法控件

星期六日閒來無事,看到之前寫的一個聊天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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章