本文是部落客對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);
}
}
若文章有什麼錯誤或者了解不到位的地方,希望各位學者不吝賜教!
另外,部落客在這祝各位事業有成,學業進步哈!