1. 程式人生 > >Android Studio:自定義Adapter(介面卡)的一些通俗易懂的理解(以一個簡單的聊天介面為例)

Android Studio:自定義Adapter(介面卡)的一些通俗易懂的理解(以一個簡單的聊天介面為例)

本文是博主對Adapter(介面卡)的一些理解,為了加深對Adapter的理解以及記錄自己的階段學習而寫,同時也適合初學者閱讀,參考本條部落格的邏輯進行學習。

第一  先來看看實現這個程式需要需要建立哪些檔案,具體的邏輯會在下文體現。 

MainActivity.java:主活動,聊天介面顯示在這個活動。

Msg.java:自定義資訊類,用於存放資訊的型別(收or發)以及資訊的內容。

MsgAdapter.java:適配RecyclerView例項的一個類。其作用是將子項(這裡指每一個msg_item.layout)與RecyclerView的一個佈局適配,這個是重點。

activity_main.xml:主活動佈局。這裡順便分享一點博主對學習AS的一點小經驗:編寫UI(User interface

)的時候先從大局考慮。例如這個介面思路:先考慮總體的佈局(activity_main),我們需要一個滾動控制元件用來裝我們的每一個子項,還需要一個可輸入的TextView以及有響應事件的傳送按鈕。接下來我們再來考慮我們的子項佈局,兩個TextView控制元件,通過判斷msg類裡資訊的型別來決定顯示and隱藏哪個TextView物件。

msg_item.xml:每一個item的佈局,上面提到過了,就不贅述了。

PS:下面會有每個檔案的程式碼貼出(附分析)。

第二   由於待會我們會用到RecyclerView(這是一個滾動控制元件,可以使這個控制元件內的每一個Item(專案;子項)實現滾動),因此首先需要在app/build.gradle當中新增依賴庫,如下所示:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.android.support:recyclerview-v7:24.2.1'
    testCompile 'junit:junit:4.12'
}

新增完依賴之後記得點選Tools—Android—Sync Project with Gradle Files進行重構(否則可能會出現找不到需要的控制元件)

第三    編寫我們的主介面activi_main.xml

<?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">
    <!-- 這裡orientation="vertical" 表示佈局是垂直的-->

    <android.support.v7.widget.RecyclerView
        android:id="@+id/msg_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"></android.support.v7.widget.RecyclerView>
    <!-- 配置了依賴庫之後就可以新增RecyclerView控制元件-->
    <!-- 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 sommething here......"
            android:maxLines="2"/>
        <!-- 可以理解為佈局裡面的佈局,對垂直佈局中某一行設定水平佈局 -->
        <!-- layout_weight同理 -->

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send"
            android:textAllCaps="false"/>
        <!-- textAllCaps="false"表示關掉文字字母全部大寫方法 -->
    </LinearLayout>

</LinearLayout>

左圖為效果圖

這個活動主要是為我們接下來的佈局定義一個最大的框架

我們在主介面中放置了一個RecyclerView用於顯示聊天的訊息內容

又放置了一個EditText用於使用者訊息輸入

還放置了一個Button用於傳送訊息。

接下來我們來建立訊息類Msg.java

(自定義資訊類,用於存放資訊的型別(收or發)以及資訊的內容)

以及他的佈局msg_item.xml

第四    建立Msg.java類以及佈局msg_item.xml

msg.java

package com.example.pjb.nine_patch;
public class Msg {
    public static  final int TYPE_RECEICED = 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;
    }//條件語句的判斷依據
}

我們將文字內容和資料型別傳給Msg的一個物件,之後在別的函式裡面讀取文字內容和判斷依據,也是對資訊包含屬性的一種封裝,這就是Msg.java的作用。

msg_item.xml(子項佈局)

<?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">
        <!-- 這裡設定了聊天框(backgroud),聊天框長度會隨傳送或者接收的資料多少來自動拉伸 -->
        <!-- 具體如何設定自動拉伸,我的推薦是解決下面兩個問題就OK了:
        1.如何在AndroidStudio裡直接使用draw9patch(AS已經集成了這個功能了,當然網上也有教AS之外使用的)
        2.如何使用draw9patch-->

        <TextView
            android:id="@+id/left_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            />
    </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>

需要注意的有兩個地方:

①第一個聊天框和第二個聊天框的的對齊方式分別是左對齊和右對齊

②也許看到這你就會產生除這樣的疑惑了:為什麼一個子項裡面要設定兩個TextView呢?怎麼能讓收到的訊息和發出的訊息都放在同一個不佈局裡呢?這樣執行的程式會不會是接受和傳送兩個聊天框重複的介面呢?其實認真看的讀者或許不會產生這種疑問,答案顯然就是上面提到過的:我們會根據資訊的型別來判斷顯示哪一個TextView。


第五    這個就是重點了。建立RecyclerView的介面卡類MsgAdapter。

先貼一張圖,看看這個類裡面有什麼東西,再來進行詳解。

類MsgAdapter繼承於RecyclerView.Adapter,並將泛型指定為自定義的內部類ViewHolder;

繼承自類RecyclerView.Adapter後重寫的幾個方法;

下面我們慢慢來理解各個函式的作用;

1.建構函式MsgAdapter

public MsgAdapter(List<Msg> msgList){
        mMsgList = msgList;
    }

在建立這個介面卡物件的時候,將所有資料都傳入,以便進行之後的操作。

2.函式getItemCount

public int getItemCount(){
        return mMsgList.size();
    }

利用建立時傳入的資料,獲取列表裡總共有多少個Item(專案)。對於這個函式的作用,我的理解是返回能被佈局的總的Item的數量。至於返回這個資料有什麼作用,我們就不必深究了,系統會自動呼叫這個函式來獲得它需要的資料。

3.內部類ViewHolder

    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);
            rightLayout = (LinearLayout)view.findViewById(R.id.right_layout);
            leftMsg = (TextView) view.findViewById(R.id.left_msg);
            rightMsg = (TextView) view.findViewById(R.id.right_msg);
        }
    }

我將內部類ViewHolder理解為檢視控制元件持有類,是一個囊括本類物件裡所有控制元件的容器,本類的作用也是為了方便,在後面不用重複去定義這些控制元件,為什麼這麼說呢?

先看程式碼,這裡有個值得注意的地方:

①它繼承於RecyclerView.ViewHolder類,這與外層MsgAdapter類相似。

②ViewHolder類還在建構函式裡呼叫了父類的建構函式,並且為每一個Item裡的所有控制元件都建立了一個對應的物件。

由此,ViewHolder類建立的物件就能夠對Item裡面的控制元件進行操作了。

這裡你可能會有疑問,建構函式中的引數是哪裡來的,系統怎麼知道需要哪個Item?

這個不用擔心,這些系統會自動幫我們做,把傳入的List<Msg>物件一個個遍歷,單獨地對每一個物件進行操作。

4.onCreaterViewHolder函式

public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false);
        return new ViewHolder(view);
    }

首先我們來閱讀下面三點。

①在實際開發種LayoutInflater這個類還是非常有用的,它的作用類似於findViewById(),不同點是LayoutInflater是用來找layout下xml佈局檔案,並且例項化!而findViewById()是找具體xml下的具體widget控制元件(如:Button,TextView等)。

②對於一個沒有被載入或者想要動態載入的介面,都需要使用inflate來載入。

③我們要知道,什麼是已經被載入的layout,什麼是還沒有載入的.我們啟動一個應用,與入口Activity相關的layout{常見的是main.xml}就是被載入的,即在Oncreate()中的。對於一個已經載入的Activity,,就可以使用實現了這個Activiyt的findViewById方法來獲得其中的介面元素.。而對於其它沒有被載入的layout,就要動態載入了或通過另一個activity。

有了上面的概念之後,這個函式就很好理解了:

這個item需要我們用inflate函式把msg_item動態的載入進main佈局,

並且返回了一個用來獲取item裡控制元件並且對其進行操作的View物件。

5.onBindViewHolder函式。Bind:捆綁;束縛 (故我理解這個函式的作用是對控制元件有約束的控制)

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

這段程式碼比較簡單:意思是通過判斷資訊型別來決定顯示或者隱藏哪個佈局。

第六    修改Mainactivity中的程式碼,為RecyclerView初始化一些資料,並給傳送按鈕加入事件響應。

package com.example.pjb.nine_patch;

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 java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private List<Msg> msgList = new ArrayList<>();
    private EditText inputText;
    private Button send;
    private RecyclerView msgRecyclerView;
    private MsgAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initMsgs();//自定義初始化資料的函式

        inputText = (EditText)findViewById(R.id.input_text);
        send = (Button)findViewById(R.id.send);
        msgRecyclerView = (RecyclerView)findViewById(R.id.msg_recycler_view);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        msgRecyclerView.setLayoutManager(layoutManager);
        adapter = new MsgAdapter(msgList);
        msgRecyclerView.setAdapter(adapter);
/*也許你做過FruitAdapter,那理解起這段程式碼來就會很輕鬆了,
 * 但是為了面向更多像博主一樣的初學者(初學者難免會遇到一些很簡單的甚至於大神都懶得回答的問題),就說的明白點。
 *ListView可以實現上下滾動,但是不能實現橫向滾動(例如微信選擇小程式時的那個橫向滾動),但是RecyclerView能夠實現。
 * 原因:ListView的佈局排列是由自身去管理的,而RecyclerView則將這個工作交給了LayoutManager,
 * LayoutManager中制定了一套可擴充套件的佈局排列介面,子類只要按照介面的規範來實現,就能定製出各種不同排列方式的佈局了。
 * 這個程式我們使用了LinearLayoutManager這種線性的佈局排列*/
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String content = inputText.getText().toString();
                if(!"".equals(content)){
                    Msg msg = new Msg(content,Msg.TYPE_SEND);
                    msgList.add(msg);
                    adapter.notifyItemChanged(msgList.size()-1);//當有新訊息時,重新整理ListView中的顯示
                    msgRecyclerView.scrollToPosition(msgList.size()-1);//將ListView定位到最後一行
                    inputText.setText("");//訊息發出後清空輸入框中的內容
                }
            }
        });
    }//事件響應
    private void initMsgs(){
        Msg msgl = new Msg("Hello guy.",Msg.TYPE_RECEICED);
        msgList.add(msgl);
        Msg msg2 = new Msg("Hello.Who is that?",Msg.TYPE_SEND);
        msgList.add(msg2);
        Msg msg3 = new Msg("This is Tom.Nice talking to you.",Msg.TYPE_RECEICED);
        msgList.add(msg3);
    }
}

若文章有什麼錯誤或者理解不到位的地方,希望各位學者不吝賜教!

另外,博主在這祝各位事業有成,學業進步哈!