天天看點

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

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

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

Android Studio:自定義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>
           
Android Studio:自定義Adapter(擴充卡)的一些通俗易懂的了解(以一個簡單的聊天界面為例)

左圖為效果圖

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

我們在主界面中放置了一個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。

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

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

類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);
    }
}
           

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

另外,部落客在這祝各位事業有成,學業進步哈!

繼續閱讀