Android Studio:自定義Adapter(介面卡)的一些通俗易懂的理解(以一個簡單的聊天介面為例)
本文是博主對Adapter(介面卡)的一些理解,為了加深對Adapter的理解以及記錄自己的階段學習而寫,同時也適合初學者閱讀,參考本條部落格的邏輯進行學習。
第一 先來看看實現這個程式需要需要建立哪些檔案,具體的邏輯會在下文體現。
MainActivity.java:主活動,聊天介面顯示在這個活動。
Msg.java:自定義資訊類,用於存放資訊的型別(收or發)以及資訊的內容。
MsgAdapter.java:適配RecyclerView例項的一個類。其作用是將子項(這裡指每一個msg_item.layout)與RecyclerView的一個佈局適配,這個是重點。
activity_main.xml:主活動佈局。這裡順便分享一點博主對學習AS的一點小經驗:編寫UI(User interface
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);
}
}
若文章有什麼錯誤或者理解不到位的地方,希望各位學者不吝賜教!
另外,博主在這祝各位事業有成,學業進步哈!