1. 程式人生 > >《第一行程式碼》 編寫精美的聊天介面

《第一行程式碼》 編寫精美的聊天介面

第一部分:9.png影象的製作

遙想當年Android2.3的時代,手機不Root那基本沒得玩,我那個時候荒廢了不少時間搞什麼系統美化,反編譯啊什麼的。雖然到頭來竹籃打水,但是也算對計算機有了一定的興趣。當時我最害怕就是9.png影象,因為只要涉及到修改這個的操作,那我是回編譯不成功的。現在的我好好的瞥了兩眼9.png的製作方法,這有什麼難的?

首先《第一行程式碼中》的方法過時了,這項功能已經被整合到AS當中了,我們把圖片放在資料夾裡面後,點選圖片右鍵選擇create 9-Patch file

隨後點選確認,在新生成的檔案上操作就行了。具體的注意事項以及重點參見.9.png中四條黑線的意義

。【注意:製作完成後,應該將原始檔案刪除,否則AS會分不清楚而報錯。】

第二部分:製作聊天介面

  • 開始之前先把RecyclerView引過來。開啟app中的build.gradle,在其中的dependencies中加上
    implementation 'com.android.support:recyclerview-v7:28.0.0'

這個與書上的不同,因為過時了。版本號與com.android.support:appcompat-v7:28.0.0相同即可。

隨後同步build.gradle

  • 因為開啟就顯示介面,所以肯定是在主佈局裡面寫。我們需要一個線性佈局,佈局應該是垂直的。上面是RecyclerCiew,底部有兩個控制元件,左邊是EditView右邊是Button,但是因為主佈局是垂直的,所以我們在佈局中再加入一個水平的佈局,底下的兩個控制元件都在水平佈局裡面。
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#d8e0e8" >
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/msg_recycle_view"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
        <!--下面的佈局是水平方向的,不寫出來預設就是水平-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
    
            <EditText
                android:id="@+id/input_text"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Type something here"
                android:maxLines="2" />
    
            <Button
                android:id="@+id/send"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Send" />
    
        </LinearLayout>
    
    </LinearLayout>
    

    這裡複習一下layout_weight的用法,我印象中這個只有在LinearLayout中才可以使用,使用之後,系統會把所有元件的weight相加,然後把空間按需分配。但是與match_parent不同的是,系統會給其他控制元件保留最小的尊嚴。比如上面的的栗子,RecyclerView的weight為1,但它下面還有輸入框和按鈕,所以系統為下面兩個控制元件留了位置。又比如下面輸入框的weight也是1,系統在水平方向上為button保留了位置。【水平狀態下,weight抵消width;垂直狀態下,weight抵消height】

  • 因為我們使用了RecyclerView,所以我們需要為其編寫子項佈局。新建一個佈局檔案,我們意識到這個佈局檔案裡面只會出現TextView,但是發出和接受訊息的方向是不同的,比如發出的都會在右面出現,接受的都會在左面出現。所以我們要宣告兩個TextView。接下來就有意思了,書上面給出的栗子是每個TextView的外層嵌套了一個LinearLayout,而如果你直接寫出來兩個TextView在一個線性佈局裡面也是可以通過的,這是兩者的區別。

對比圖

可以看得出來,沒有巢狀的 字幾乎是貼著圖片的,而巢狀著的顯得更加的自然。這就是與郭神的差距啊。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp" >

    <LinearLayout
        android:id="@+id/left_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:background="@drawable/message_left" >

        <TextView
            android:id="@+id/left_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:textColor="#fff" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/right-layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:background="@drawable/message_right" >

        <TextView
            android:id="@+id/right_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp" />

    </LinearLayout>

</LinearLayout>

這裡說一下layout_margin的含義,它線上性佈局裡的意思是view距離父view的距離。正因為如此,TextView才可以與巢狀著的LinearLayout形成縫隙,這是不巢狀沒法做出來的。

  • 第三部分:RecyclerView介面卡和資訊類

資訊類Msg,我們的訊息分為接收的訊息和傳送的訊息,他們唯一的區別就是在螢幕上的位置不同,因此我們需要定義訊息的狀態,int就可以。有了資訊的狀態,當然還需要資訊的內容啊,接下來定義幾個接收器就可以了。

package com.example.uibestpractice;

public class Msg {
    //普通的記錄訊息的類
    public static final int TYPE_RECEIVED=0;
    public static final int TYPE_SEND=1;
    private String content;
    private int Type;
    public Msg(String content,int Type)
    {
        this.content=content;
        this.Type=Type;
    }
    public String getContent()
    {
        return content;
    }
    public int getType()
    {
        return Type;
    }

}
  • 製作RecyclerView的介面卡

開始製作介面卡,都是一樣的套路,首先宣告一個類MsgAdapter繼承RecyclerView.Adapter<MsgAdapter.ViewHolder>,其中ViewHolder是我們定義在類中的一個靜態類,繼承自RecyclerView.ViewHolder,在這個靜態類中我們需要例項化我們RecyclerView佈局中的控制元件,有兩個LinearLayout和兩個TextView,都在建構函式中進行,但是建構函式必須滿足父類的建構函式,因此傳一個view進去就可以了。

我們的主類的建構函式很簡單,只需要接收一個List即可,將接受的List傳給欄位。這個List裡面儲存的都是Msg類的物件。

隨後重寫 onCreateViewHolder,onBindViewHolder以及getItemCount三種方法。先來說最簡單的getItemCount,只需要返回list的size即可。

接著看onCreateVIewHolder,這個方法是動態RecyclerView例項的,所以宣告

View view =LayoutInflater.from(paremt.context).inflate(子項id,parent,false);
retuen new ViewHolder(view);

再來onBindViewHolder,這個是要賦給內部類欄位屬性,首先先建立對應position的Msg的物件,如果這個物件的是接受的訊息,那麼我們就把對應傳送訊息的LinearLayout,view.GONE掉。,顯示出來接受訊息的LinearLayout。注意一定是GONE,不是INVISIBLE,否則後果如下:

上正確的程式碼

package com.example.uibestpractice;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.List;

public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> {
    private List<Msg> mMsgList;
    static class ViewHolder extends RecyclerView.ViewHolder
    {
        LinearLayout leftLayout;
        LinearLayout rightLayout;
        TextView leftMsg;
        TextView rightMsg;
        public ViewHolder(View view)
        {
            super(view);
            leftLayout=(LinearLayout) view.findViewById(R.id.left_layout);//因為layout是在檔案中,所以是R.id
            rightLayout=(LinearLayout) view.findViewById(R.id.right_layout);
            leftMsg=(TextView) view.findViewById(R.id.left_msg);
            rightMsg=(TextView) view.findViewById(R.id.right_msg);
        }
    }
    public MsgAdapter(List<Msg> mMsgList){
    this.mMsgList=mMsgList;
    }

    @Override
    public ViewHolder onCreateViewHolder( ViewGroup parent, int i) {
        View view=LayoutInflater.from(parent.getContext()).inflate(i,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Msg msg=mMsgList.get(position);
        if(msg.getType()== Msg.TYPE_RECEIVED)
        {
            holder.leftLayout.setVisibility(View.VISIBLE);
            holder.rightLayout.setVisibility(View.GONE);
            holder.leftMsg.setText(msg.getContent());
        }
        else if (msg.getType()==Msg.TYPE_SEND)
        {
            holder.rightLayout.setVisibility(View.VISIBLE);
            holder.leftLayout.setVisibility(View.GONE);
            holder.rightMsg.setText(msg.getContent());
        }
    }

    @Override
    public int getItemCount() {
        return mMsgList.size();
    }
}
  • 最後就是在主類中例項化了​​​​

​​​​​​​首先例項化RecyclerView;因為我們要點選button,所以也有它的份;例項化button為了獲得EditView的內容啊,所以這個編輯框也得例項化;例項化介面卡;最後規定RecyclerView的格式,這次依然是傳統格式。

我們還需要為button建立一個監聽器,我們需要將編輯框的內容傳送到RecyclerView上

①獲取內容,將字串新增到List當中去。

②呼叫介面卡的notifyItemInserted方法,通知有新的資料加入了,趕緊將這個資料加到RecyclerView上面去。

③呼叫RecyclerView的scrollToPosition方法,以保證一定可以看的到最後發出的一條訊息。

最後我沒有按照書上的栗子初始化List,修改了一下,現在你可以自己和自己聊天了。

package com.example.uibestpractice;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private List<Msg> msgList=new ArrayList<>();
    private EditText editText;
    private Button button;
    private RecyclerView recyclerView;
    private  MsgAdapter adapter;
    private boolean Send=true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       editText=(EditText) findViewById(R.id.input_text);
       button=(Button) findViewById(R.id.send);
       recyclerView=(RecyclerView) findViewById(R.id.msg_recycle_view);
       adapter=new MsgAdapter(msgList);
        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(adapter);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String content=editText.getText().toString();
                if (!"".equals(content))
                {
                    Msg msg;
                    if (Send)
                    { msg=new Msg(content,Msg.TYPE_SEND);}
                    else
                    { msg=new Msg(content,Msg.TYPE_RECEIVED);
                    }
                    Send=!Send;
                    msgList.add(msg);
                    adapter.notifyItemInserted(msgList.size()-1);
                    recyclerView.scrollToPosition(msgList.size()-1);
                    editText.setText("");
                }
            }
        });
}

希望所有認真的同學都能有一個好的結果,祝你晚安。

                                                                                                                                  ------2018.10.3於圖書館