1. 程式人生 > >類似QQ聊天介面

類似QQ聊天介面

現在越來越多的手機軟體具備社交聊天功能,所以聊天介面的使用便變得很頻繁,下面我們將自己實現一個簡單的類似QQ的聊天介面。
首先來看整個工程的目錄結構:
這裡寫圖片描述
目錄結構很簡單,主要難點在Adapter。
然後看實現的效果圖:
這裡寫圖片描述
從效果圖上看出,介面底部用了一個EditText和一個Button,水平分佈,上面放了一個RecyclerView,佈局程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:background="#ede9e9" android:orientation="vertical" tools:context="com.demo.sisyphus.hellorobot.MainActivity">
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:orientation="vertical">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="機器人" android:textSize="10pt" android:gravity="center_horizontal"/> <android.support.v7.widget.RecyclerView android:id="@+id/rv_chat" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_alignParentBottom="true"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/rect" android:layout_weight="1" android:layout_marginRight="5dp" android:id="@+id/et_msg"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_send" android:text="@string/btnsend"/> </LinearLayout> </LinearLayout>

然後看這篇文章的重點Adapter,用過RecyclerView的都知道,它的資料處理都在Adapter,類似ListView的Adapter,但是更加強大,程式碼如下:

public class ChatAdapter extends RecyclerView.Adapter {

    private Context context;

    private static final int ME = 0;
    private static final int OTHRE = 1;

    private List<Msg> list = new ArrayList<>();

    public ChatAdapter(Context context, ArrayList<Msg> list){
        this.context = context;
        this.list = list;
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        LinearLayout me;
        LinearLayout other;

        public ViewHolder(View itemView) {
            super(itemView);
            me = (LinearLayout) itemView.findViewById(R.id.me);
            other = (LinearLayout) itemView.findViewById(R.id.other);
        }

        public LinearLayout getMe() {
            return me;
        }

        public LinearLayout getOther() {
            return other;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        ViewHolder viewHolder = null;

        switch (viewType){
            case ME:
                viewHolder = new ViewHolder(LayoutInflater.from(context).inflate(R.layout.chat_item2, parent, false));
                break;
            case OTHRE:
                viewHolder = new ViewHolder(LayoutInflater.from(context).inflate(R.layout.chat_item, parent, false));
                break;
        }

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ViewHolder viewHolder = (ViewHolder) holder;

        TextView tv = new TextView(context);

        Msg msg = list.get(position);

        tv.setText(msg.getMsg());

        switch (msg.getType()){
            case ME:
                /**
                 * 當recyclerview承載的資料過多的時候,去滑動recyclerview,
                 * 劃出螢幕以外的item會重新繫結資料,如果這個時候繫結資料的方式是
                 * viewgroup的addView()方法的話,會出現item新增很多重複的view
                 * 所以這之前應該執行清除裡面view的操作,即removeAllViews()
                 */
                viewHolder.getMe().removeAllViews();
                tv.setBackgroundResource(R.mipmap.chat_me);
                viewHolder.getMe().addView(tv);
                break;
            case OTHRE:
                viewHolder.getOther().removeAllViews();
                tv.setBackgroundResource(R.mipmap.chat_other);
                viewHolder.getOther().addView(tv);
                break;
        }
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    @Override
    public int getItemViewType(int position) {
        return list.get(position).getType() == 0 ? ME : OTHRE;
    }

    public void addMsg(Msg msg){
        list.add(msg);
    }
}

因為是聊天介面,所以需要RecyclerView根據發出訊息的人是誰來判斷這條訊息是顯示左邊還是右邊,因此就需要根據ViewType的值來載入兩個不同的佈局檔案,在這個專案中分別是chat_item.xml和chat_item2.xml。而訊息和訊息型別的判斷我們封裝成一個物件,即Msg類:

public class Msg {

    private String msg;
    private int type;

    public Msg(String msg, int type){
        this.msg = msg;
        this.type = type;
    }

    public String getMsg() {
        return msg;
    }

    public int getType() {
        return type;
    }
}

這個類只有兩個屬性,很簡單,主要承載訊息內容和訊息型別(來自誰),訊息內容可根據實際需要擴充套件為圖片、音訊之類的。
最後在Activity裡面,將所有控制元件初始化之後,我們就實現了這個簡單的聊天介面。
另外,為了使這個聊天介面聊天介面更加具有可用性,我們可以給訊息加上定位的效果,即當訊息過多時,RecyclerView自動滑動到當前訊息的位置,使用RecyclerView的smoothScrollToPosition(position)方法即可實現,很簡單。
最後補充兩個點:

**1. 圓形頭像
這個圓形頭像使用了一個開源庫RoundedImageView
android studio直接在專案上右鍵-Open Module Settings-Dependencies-Add Library dependency,直接搜尋RoundedImageView就可以了,選擇最新版本。
使用很簡單,引入RoundedImageView控制元件,設定其屬性就可以得到多種形狀的圖片了。
2. 我做這個專案遇到的一個問題
在做這個Demo時,我發現,當聊天資料量達到十多條時,滑動RecyclerView,一些item會一直重複新增資料項,後來我發現是繫結資料的時候沒有考慮周全,出現問題的地方就是我Adapter寫註釋的地方,原因和解決辦法可以參考註釋。
好了,一個簡單的聊天介面就大功告成,感興趣的朋友可以點選這裡下載原始碼,我放在github上的。
可能還有很多程式碼Bug和說明不正確的地方,請批評指正。**