今天要來聊的又是RecyclerView (・∀・)(・∀・)(・∀・)
RecyclerView本網誌截至今天是第五篇
一樣先把我所有寫過的RecyclerView連結貼上來吧
碼農日常-『Android studio』基本RecyclerView用法
碼農日常-『Android studio』基本RecyclerView 用法-2 基本版下拉更新以及點擊事件
碼農日常-『Android studio』基本RecyclerView 用法-3 RecyclerView上下滑動排序與側滑刪除(RecyclerView Swipe)
碼農日常-『Android studio』基本RecyclerView 用法-4 左滑顯示Button Menu
今天要寫的內容我把它叫做"RecyclerView 混合介面/佈局"
當初我在找資料的時候是用中文搜尋到的
也在這邊放上參考資料
->https://www.jianshu.com/p/c9ce1c67981d
這篇的搜尋結果在Google排名上是前幾的,應該很容易可以搜尋到
keyWord:RecyclerView混合佈局
簡單來說今天的內容就是要在一個RecyclerView裡面加入兩種(以上)的佈局
像這樣
不用懷疑,這是只用一個RecyclerView做出來的哦(・ω・)b
那兩個介面在哪裡呢?
第一個介面就是標題A、B、C的那個部分
而第二個介面是顯示英文單字以及翻譯按鈕的那個介面
那麽理解了今天要講的內容的話,就來上今天的Gif功能吧
以及Github
->https://github.com/thumbb13555/RecyclerViewMixitemExample
開始~
1.先建立好所有需要的介面&類別
不知有否注意到今天的標題?
"碼農日常-『Android studio』進階RecyclerView 用法-5 RecyclerView item混合介面"
這次的標題,我不再寫"基本"而是"進階"(・ωー)~☆
主要原因很簡單,因為這次的內容不是一個類別可以完成的內容
而我在撰寫的過程中,也是用較貼近平常工作的寫法下去寫
希望不會對你造成太多的不適應QQ(當然,我也會很努力地讓你就算不理解也能實作出來)
那就真正開始今天要做的內容吧(•ө•)♡
1-1. 建立介面
今天要建立三個介面,分別如下
1. activity_main.xml
2. a_type_item.xml
3. b_type_item.xml
activity_main 就是主要畫面的RecyclerView
a_type_item 則是RecyclerView的第一種介面
b_type_item 則是RecyclerView的第二種介面
會來看這篇文的應該都應該不是新手,建立介面過程省略啦wwwww
那就奉上這三個介面吧
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/textView_Word" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:text="TextView" android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textSize="18sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button_GetChinese" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:text="顯示中文" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
1-2. 建立所需的類別
接下來就是類別了,我直接先給各位看看所有的類別吧
其中MyViewHolders、A_TypeMyView、B_TypeMyView都是給item介面用的
其中以上所有類別之間的關係為
在這邊建議如果是完全不會的朋友,想要模仿我做的內容
可以先照著我圖片中的類別先建立起來,然後接下來就一個一個講
2.撰寫功能
齁...要來撰寫功能了(吸氣...)
首先要先來寫MainActivity的部分(`・ω・´)+
其中我有製作一些單字用的資料,如何寫的不是重點(當然我會PO)
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); /**設置recyclerView有分隔線*/ recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); MyRecyclerViewAdapter mAdapter = new MyRecyclerViewAdapter(makeData()); recyclerView.setAdapter(mAdapter); } /**製作單字資料*/ private ArrayList<HashMap<String,String>> makeData(){ ArrayList<HashMap<String,String>> arrayList = new ArrayList<>(); String[] vocabulary = {"#A","aptitude","apprentice" ,"#B","bilingual","banquet" ,"#C","credential","coordinator","contain","continued" ,"#D","distributor","deviate" ,"#E","enhancement","election","engrave","excess" ,"#F","factor"}; String[] chinese = {"#A","才能、資質","實習生" ,"#B","雙語的","宴會" ,"#C","證書","協調者","包含","持續的" ,"#D","分配者、分銷商","偏離" ,"#E"," 提升、改善","選舉","雕刻","超量" ,"#F","要素"}; for (int i = 0; i < vocabulary.length; i++) { HashMap<String,String> hashMap = new HashMap<>(); if (vocabulary[i].charAt(0) == '#'){ hashMap.put("ENGLISH",vocabulary[i].replace("#","")); hashMap.put("CHINESE","--"); hashMap.put("VIEW_TYPE","0"); }else{ hashMap.put("ENGLISH",vocabulary[i]); hashMap.put("CHINESE",chinese[i]); hashMap.put("VIEW_TYPE","1"); } arrayList.add(hashMap); } return arrayList; }//makeData }
這時候,如果你檢視一下輸出的內容,大概就是長這樣:
再來,來到MyRecyclerViewAdapter.java
class MyRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private ArrayList<HashMap<String,String>> arrayList; public OnTransClick onItemClick; public MyRecyclerViewAdapter(ArrayList<HashMap<String,String>> arrayList) { this.arrayList = arrayList; } /**設置將資料傳回Activity的接口*/ public void setOnTransButtonClick(OnTransClick onItemClick){ this.onItemClick = onItemClick; } /**取得每個item內的"VIEW_TYPE"*/ @Override public int getItemViewType(int position) { int getType = Integer.parseInt(arrayList.get(position).get("VIEW_TYPE")); return getType; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { /**在上面的"getItemViewType"中取得的 * @see viewType * 為基準,判斷每個item需使用哪個介面*/ if (viewType == 1) { return new B_TypeMyView(LayoutInflater.from(parent.getContext()) .inflate(R.layout.b_type_item, parent, false)); } else { return new A_TypeMyView(LayoutInflater.from(parent.getContext()) .inflate(R.layout.a_type_item, parent, false)); } } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { /**將holder強制轉型為"MyViewHolders"類別,使介面A/B都可以獲得onBindViewHolder內容*/ ((MyViewHolders) holder).bindViewHolder(arrayList.get(position)); } @Override public int getItemCount() { return arrayList.size(); } /**設置點擊方法,使點擊後取得到的內容能傳回MainActivity*/ public interface OnTransClick{ void OnTransButtonClick(HashMap<String,String> hashMap); } }
關鍵點是在粉底的部分
那個類別叫做getItemViewType,回傳的值會出現在onCreateViewHolder的viewType內(綠底)
而其中getItemViewType可以理解為他會幫你跑一次你所輸入的陣列,因此可以偵測到你所輸入的判斷依據
再仔細回頭看我們前面PO過的資料輸出
這樣大致可以理解了嗎?(¬_¬)ノ
這時候你會發現綠底的部分會出錯
是因為我們沒有寫三個介面的內容~
所以以下直接寫MyViewHolders、A_TypeMyView、B_TypeMyView吧
public abstract class MyViewHolders extends RecyclerView.ViewHolder { public MyViewHolders(@NonNull View itemView) { super(itemView); } /**建立抽象類別,並使onBindViewHolder可分別綁定到介面A與介面B*/ public abstract void bindViewHolder(HashMap<String,String> hashMap); }
public class A_TypeMyView extends MyViewHolders { public TextView tvTitle; public A_TypeMyView(@NonNull View itemView) { super(itemView); tvTitle = itemView.findViewById(R.id.textView_ATItle); } /**將資料綁到介面A的內容*/ @Override public void bindViewHolder(HashMap<String, String> hashMap) { tvTitle.setText(hashMap.get("ENGLISH")); } }
public class B_TypeMyView extends MyViewHolders { public Button btTrans; public TextView tvShow; public B_TypeMyView(@NonNull View itemView) { super(itemView); btTrans = itemView.findViewById(R.id.button_GetChinese); tvShow = itemView.findViewById(R.id.textView_Word); } /**將資料綁到介面B的內容*/ @Override public void bindViewHolder(HashMap<String, String> hashMap) { tvShow.setText(hashMap.get("ENGLISH")); } }
好,這時候可以試者執行一下
這時候就可以看到基本介面囉d(>_・ )
3.撰寫點擊事件
這次要設置點擊事件了
其實我都已經把點擊該做的事都寫好了XD
其主要的內容主要寫在MyRecyclerViewAdapter與MainActivity內
那我就來標注一下MyRecyclerViewAdapter的點擊相關吧
class MyRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private ArrayList<HashMap<String,String>> arrayList; public OnTransClick onItemClick; public MyRecyclerViewAdapter(ArrayList<HashMap<String,String>> arrayList) { this.arrayList = arrayList; } /**設置將資料傳回Activity的接口*/ public void setOnTransButtonClick(OnTransClick onItemClick){ this.onItemClick = onItemClick; } /**取得每個item內的"VIEW_TYPE"*/ @Override public int getItemViewType(int position) { int getType = Integer.parseInt(arrayList.get(position).get("VIEW_TYPE")); return getType; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { /**在上面的"getItemViewType"中取得的 * @see viewType * 為基準,判斷每個item需使用哪個介面*/ if (viewType == 1) { return new B_TypeMyView(LayoutInflater.from(parent.getContext()) .inflate(R.layout.b_type_item, parent, false)); } else { return new A_TypeMyView(LayoutInflater.from(parent.getContext()) .inflate(R.layout.a_type_item, parent, false)); } } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { /**將holder強制轉型為"MyViewHolders"類別,使介面A/B都可以獲得onBindViewHolder內容*/ ((MyViewHolders) holder).bindViewHolder(arrayList.get(position)); /**判斷該item的介面是處於哪一個介面*/ if (holder instanceof A_TypeMyView){ //你可以試著為他加入點擊事件~ }else if (holder instanceof B_TypeMyView){ B_TypeMyView bTypeMyView = (B_TypeMyView) holder; /**設置翻譯按鈕的點擊事件*/ bTypeMyView.btTrans.setOnClickListener(v->{ onItemClick.OnTransButtonClick(arrayList.get(position)); }); } } @Override public int getItemCount() { return arrayList.size(); } /**設置點擊方法,使點擊後取得到的內容能傳回MainActivity*/ public interface OnTransClick{ void OnTransButtonClick(HashMap<String,String> hashMap); } }
最後,將資訊傳至MainActivity吧(此處為MainActivity.java)
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); /**設置recyclerView有分隔線*/ recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); MyRecyclerViewAdapter mAdapter = new MyRecyclerViewAdapter(makeData()); recyclerView.setAdapter(mAdapter); /**從MyRecyclerViewAdapter取得資料的點擊事件*/ mAdapter.setOnTransButtonClick(hashMap -> { String getTrans = hashMap.get("CHINESE"); Toast.makeText(this, getTrans, Toast.LENGTH_SHORT).show(); });//Click }
這時候按下執行,就可以看到想要的內容囉
最後放上所有的類別吧!
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); /**設置recyclerView有分隔線*/ recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); MyRecyclerViewAdapter mAdapter = new MyRecyclerViewAdapter(makeData()); recyclerView.setAdapter(mAdapter); /**從MyRecyclerViewAdapter取得資料的點擊事件*/ mAdapter.setOnTransButtonClick(hashMap -> { String getTrans = hashMap.get("CHINESE"); Toast.makeText(this, getTrans, Toast.LENGTH_SHORT).show(); });//Click } /**製作單字資料*/ private ArrayList<HashMap<String,String>> makeData(){ ArrayList<HashMap<String,String>> arrayList = new ArrayList<>(); String[] vocabulary = {"#A","aptitude","apprentice" ,"#B","bilingual","banquet" ,"#C","credential","coordinator","contain","continued" ,"#D","distributor","deviate" ,"#E","enhancement","election","engrave","excess" ,"#F","factor"}; String[] chinese = {"#A","才能、資質","實習生" ,"#B","雙語的","宴會" ,"#C","證書","協調者","包含","持續的" ,"#D","分配者、分銷商","偏離" ,"#E"," 提升、改善","選舉","雕刻","超量" ,"#F","要素"}; for (int i = 0; i < vocabulary.length; i++) { HashMap<String,String> hashMap = new HashMap<>(); if (vocabulary[i].charAt(0) == '#'){ hashMap.put("ENGLISH",vocabulary[i].replace("#","")); hashMap.put("CHINESE","--"); hashMap.put("VIEW_TYPE","0"); }else{ hashMap.put("ENGLISH",vocabulary[i]); hashMap.put("CHINESE",chinese[i]); hashMap.put("VIEW_TYPE","1"); } arrayList.add(hashMap); } return arrayList; }//makeData }
class MyRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private ArrayList<HashMap<String,String>> arrayList; public OnTransClick onItemClick; public MyRecyclerViewAdapter(ArrayList<HashMap<String,String>> arrayList) { this.arrayList = arrayList; } /**設置將資料傳回Activity的接口*/ public void setOnTransButtonClick(OnTransClick onItemClick){ this.onItemClick = onItemClick; } /**取得每個item內的"VIEW_TYPE"*/ @Override public int getItemViewType(int position) { int getType = Integer.parseInt(arrayList.get(position).get("VIEW_TYPE")); return getType; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { /**在上面的"getItemViewType"中取得的 * @see viewType * 為基準,判斷每個item需使用哪個介面*/ if (viewType == 1) { return new B_TypeMyView(LayoutInflater.from(parent.getContext()) .inflate(R.layout.b_type_item, parent, false)); } else { return new A_TypeMyView(LayoutInflater.from(parent.getContext()) .inflate(R.layout.a_type_item, parent, false)); } } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { /**將holder強制轉型為"MyViewHolders"類別,使介面A/B都可以獲得onBindViewHolder內容*/ ((MyViewHolders) holder).bindViewHolder(arrayList.get(position)); /**判斷該item的介面是處於哪一個介面*/ if (holder instanceof A_TypeMyView){ //你可以試著為他加入點擊事件~ }else if (holder instanceof B_TypeMyView){ B_TypeMyView bTypeMyView = (B_TypeMyView) holder; /**設置翻譯按鈕的點擊事件*/ bTypeMyView.btTrans.setOnClickListener(v->{ onItemClick.OnTransButtonClick(arrayList.get(position)); }); } } @Override public int getItemCount() { return arrayList.size(); } /**設置點擊方法,使點擊後取得到的內容能傳回MainActivity*/ public interface OnTransClick{ void OnTransButtonClick(HashMap<String,String> hashMap); } }
public abstract class MyViewHolders extends RecyclerView.ViewHolder { public MyViewHolders(@NonNull View itemView) { super(itemView); } /**建立抽象類別,並使onBindViewHolder可分別綁定到介面A與介面B*/ public abstract void bindViewHolder(HashMap<String,String> hashMap); }
public class A_TypeMyView extends MyViewHolders { public TextView tvTitle; public A_TypeMyView(@NonNull View itemView) { super(itemView); tvTitle = itemView.findViewById(R.id.textView_ATItle); } /**將資料綁到介面A的內容*/ @Override public void bindViewHolder(HashMap<String, String> hashMap) { tvTitle.setText(hashMap.get("ENGLISH")); } }
public class B_TypeMyView extends MyViewHolders { public Button btTrans; public TextView tvShow; public B_TypeMyView(@NonNull View itemView) { super(itemView); btTrans = itemView.findViewById(R.id.button_GetChinese); tvShow = itemView.findViewById(R.id.textView_Word); } /**將資料綁到介面B的內容*/ @Override public void bindViewHolder(HashMap<String, String> hashMap) { tvShow.setText(hashMap.get("ENGLISH")); } }
結語
前面也說了,今天的內容是比較進階的的內容
今天之所以會寫這個,是因為我之前在找這方面的資料的時候
實在是非常不滿我們台灣沒有人寫這方面的資料,我真的覺得非常可惜OTL((也許有啦,只是沒找到
唉...有時候真的很感慨,大陸就是人多,所以技術文章也多
台灣最近有出了個IT邦,但是畢竟歷史不夠久,人口基數也不夠多
所以技術文章的廣度跟深度還是有限,真的很可惜(´υ`)
那麼希望今天的這篇文章可以幫助到你,祝各位程式沒有Bug