今天來寫關於嵌(ㄑㄧㄢ)入式RecyclerView,又稱為巢狀RecyclerView

巢狀RecyclerView簡單來說就是在原先的RecyclerView列表元件中在套入一層RecyclerView的意思(・ω・)b

這個啊~我敢跟各位打包票

有拿智慧型手機的一定有看過其應用

 

就是Google Play啦!!

截圖 2021-04-24 下午9.11.39

 

沒錯,GooglePlay他就是可以直向滑動,也可以橫向滑動對吧

那麼,就來對照一下今天的範例吧

 

 

以及Github

https://github.com/thumbb13555/NestedRecyclerDemo/tree/master

 

補充一下,這個也是本站中的RecyclerView系列喔

該系列文有以下

碼農日常-『Android studio』基本RecyclerView用法

碼農日常-『Android studio』基本RecyclerView 用法-2 基本版下拉更新以及點擊事件

碼農日常-『Android studio』基本RecyclerView 用法-3 RecyclerView上下滑動排序與側滑刪除(RecyclerView Swipe)

碼農日常-『Android studio』基本RecyclerView 用法-4 左滑顯示Button Menu

碼農日常-『Android studio』進階RecyclerView 用法-5 RecyclerView item混合介面

歡迎參考參考~

 


 

1. 專案結構

 

這次的內容有點小複雜~畢竟一口氣寫了四個檔案

如下

截圖 2021-04-25 上午12.20.58

 

這次的內容,簡單來說兩個檔案MyAdapter跟NestedAdapter都是模組,都是RecyclerView的適配器

MyAdapter主要的那個RecyclerView,而NestedAdapter則是嵌套在主要的RecyclerView裡面的那個

再來,MyData則是他們要吃的格式,我下面兩個單元會再詳細說明內容以及製作資料的部分

 


 

2. 介面繪製

 

有三個介面,分別是主介面main、主RecyclerView的Item跟嵌套RecyclerVIew的item

分別如下

截圖 2021-04-25 上午12.37.13

 

activity_main.xml

<?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>

就一個RecyclerView而已,沒什麼特別的

 

item.xml

截圖 2021-04-25 上午12.42.17

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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="wrap_content"
    app:cardCornerRadius="10dp"
    app:cardElevation="7dp"
    android:layout_margin="5dp"
    >
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="5dp"
        >

        <TextView
            android:id="@+id/textView_Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="TextView"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            android:textStyle="bold"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerview_Nested"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:orientation="horizontal"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView_Title" />
    </androidx.constraintlayout.widget.ConstraintLayout>


</androidx.cardview.widget.CardView>

這裡有點特別!!要注意我粉底白字的部分

以往我都習慣把LayoutManager交給程式碼去弄

不過這個地方要在一開始就先定義好LayoutManager

並且orientation(定向)要事先設定為horizontal(橫向),設定完後介面會即使顯示出來給你看

這部分要特別注意喔!

 

item_nested.xml

截圖 2021-04-25 上午12.47.29

<?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="wrap_content"
    android:orientation="horizontal"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/cardview_shadow_start_color"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/ic_launcher_foreground" />

    <TextView
        android:id="@+id/textView_nesTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:lineSpacingExtra="14sp"
        android:text="TextView"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="@+id/imageView"
        app:layout_constraintStart_toStartOf="@+id/imageView"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

這邊也沒什麼要注意的,就直接複製就好囉

 


 

3. 建立實體類MyData.java以及製作測試資料 

 

接下來是撰寫資料及建立實體類MyData.java

建立過程省略,我直接PO內容給你

 

MyData.java

class MyData {
    private String title;
    private List<NestedData> nesData;

    public MyData(String title, List<NestedData> nesData) {
        this.title = title;
        this.nesData = nesData;
    }

    public String getTitle() {
        return title;
    }

    public List<NestedData> getNesData() {
        return nesData;
    }

    static class NestedData{
        private String nesTitle;

        public NestedData(String title) {
            this.nesTitle = title;
        }

        public String getNesTitle() {
            return nesTitle;
        }
    }
}

 

再來是測試資料,下面有放playground可以執行起來玩看看

 

 

執行起來會像這個樣子喔

截圖 2021-04-25 上午12.54.19

 

至於這段程式寫在MainActivity裡面,我先把那部分PO出來~

public class MainActivity extends AppCompatActivity {

    MyAdapter myAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**製作資料*/
        List<MyData> data = makeData();
    }

    private List<MyData> makeData(){
        List<MyData> data = new ArrayList<>();
        String[] title = new String[]{"週一","週二","週三","週四","週五"};
        String[] nesTitle = new String[]{"清燉牛肉","番茄炒蛋","炸雞腿","糖醋魚","焗烤燉飯","爆炒蝦仁","紅燒鮭魚"};

        for (int i = 0; i < title.length; i++) {
            int r = (int)(Math.random()*7);
            List<MyData.NestedData> nesArray = new ArrayList<>();
            for (int j = 0; j < r+1; j++) {
                nesArray.add(new MyData.NestedData(nesTitle[(int)(Math.random()*7)]));
            }
            data.add(new MyData(title[i],nesArray));
        }
        return data;
    }
}

 


 

4. 撰寫嵌套的Adapter(NestedAdapter.java) 

 

接下來是重點了,再來要來寫RecylerView的Adapter

順序上我是從最裡面的那個一層一層寫

所以我們先寫被嵌入的那一層~

 

NestedAdapter.java

class NestedAdapter extends RecyclerView.Adapter<NestedAdapter.NesHolder> {

    private List<MyData.NestedData> nestedData;
    private OnChildClick childClick;
    private int parentPosition;

    public NestedAdapter(List<MyData.NestedData> nestedData,int parentPosition, OnChildClick childClick) {
        this.nestedData = nestedData;
        this.childClick = childClick;
        this.parentPosition = parentPosition;
    }

    public class NesHolder extends RecyclerView.ViewHolder {
        TextView tvTitle;

        public NesHolder(@NonNull View itemView) {
            super(itemView);
            tvTitle = itemView.findViewById(R.id.textView_nesTitle);
        }
    }

    @NonNull
    @Override
    public NesHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new NesHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_nested, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull NesHolder holder, int position) {

        MyData.NestedData item = nestedData.get(position);
        holder.tvTitle.setText(item.getNesTitle());

        holder.itemView.setOnClickListener(v -> {
            //取得點擊到的item,並使用interface回傳
            childClick.onChildClick(item,parentPosition);
        });
    }

    @Override
    public int getItemCount() {
        return nestedData.size();
    }

    interface OnChildClick {
        void onChildClick(MyData.NestedData data,int parentPosition);
    }

}

 

這層比較沒什麼特別的,基本上就是一個普通的RecyclerVIew寫法

而由於這次的內容還包含點擊事件,因此我加入了Interface以便後續撰寫

關於這方面不太了解的,可以慘考我這篇文章喔

碼農日常-『Android studio』NumberPicker 配合 Interface (接口)完成一個時間選擇器

 


 

4. 撰寫主Adapter(MyAdapter.java) 

 

這裏就是比較需要注意的地方了

不過一樣先貼

MyAdapter.java

class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> implements NestedAdapter.OnChildClick {

    private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
    private List<MyData> myData;
    private OnItemClick onItemClick;
    public MyAdapter(List<MyData> myData,OnItemClick onItemClick) {
        this.myData = myData;
        this.onItemClick = onItemClick;
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        private TextView tvTitle;
        private RecyclerView recyclerView;
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvTitle = itemView.findViewById(R.id.textView_Title);
            recyclerView = itemView.findViewById(R.id.recyclerview_Nested);
        }
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MyViewHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item,parent,false));
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvTitle.setText(myData.get(position).getTitle());
        //設置巢狀RecyclerView
        holder.recyclerView.setAdapter(new NestedAdapter(myData.get(position).getNesData()
                ,position,this));
        holder.recyclerView.setRecycledViewPool(viewPool);

    }

    @Override
    public int getItemCount() {
        return myData.size();
    }
    /**此處為點選Child-Item後從
     * @see NestedAdapter 的回傳*/

    @Override
    public void onChildClick(MyData.NestedData data, int parentPosition) {
        onItemClick.onItemClick(data,myData.get(parentPosition));
    }
    interface OnItemClick{
        void onItemClick(MyData.NestedData data,MyData myData);
    }
}

 

首先是藍底白字的部分

這是RecyclerView底下的一個類別叫RecyclerViewPool

以往只要命名上出現Pool的方法大多跟緩加載有關聯

沒錯,這個方法的確就是優化執行緒用的

綠底白字的部分則一樣是將recyclerView綁定Adapter用的

最後一樣是Interface的設置,我就不再囉唆了

 


 

5. 撰寫主程式(MainActivity.java) 

 

以上都設置好模組了,接下來就是只剩使用它們!(-‿◦)

總之廢話不多說,填入以下內容即完成(;´д`)ゞ

 

MainActivity.java

public class MainActivity extends AppCompatActivity {

    MyAdapter myAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**製作資料*/
        List<MyData> data = makeData();
        /**設置RecyclerView*/
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        //設置Adapter以及點擊回饋
        myAdapter = new MyAdapter(data, new MyAdapter.OnItemClick() {
            @Override
            public void onItemClick(MyData.NestedData data, MyData myData) {
                Toast.makeText(MainActivity.this
                        , "選擇了"+myData.getTitle()+"的 "+data.getNesTitle()
                        , Toast.LENGTH_SHORT).show();
            }
        });
        recyclerView.setAdapter(myAdapter);

    }

    private List<MyData> makeData(){
        List<MyData> data = new ArrayList<>();
        String[] title = new String[]{"週一","週二","週三","週四","週五"};
        String[] nesTitle = new String[]{"清燉牛肉","番茄炒蛋","炸雞腿","糖醋魚","焗烤燉飯","爆炒蝦仁","紅燒鮭魚"};

        for (int i = 0; i < title.length; i++) {
            int r = (int)(Math.random()*7);
            List<MyData.NestedData> nesArray = new ArrayList<>();
            for (int j = 0; j < r+1; j++) {
                nesArray.add(new MyData.NestedData(nesTitle[(int)(Math.random()*7)]));
            }
            data.add(new MyData(title[i],nesArray));
        }
        return data;
    }
}

 

我寫的文章還有附帶點擊事件喔~很有誠意吧XD

 


 

這篇我有一點點隨便寫(先承認

因為其實今天在Coding遇到了一點小問題,基本上就是顯示結果有些不如預期這樣

不過當你看到這篇文章時,表示我當幫你克服囉XD

後來又東摸西摸..最後搞得這篇半夜才寫完(╯=▃=)╯︵┻━┻

 

而且寫完Code後要寫文章時,莫名其妙又跑去弄網誌的簽名檔,一整個不知道自己在衝三毀( ´_ゝ`)

不過簽名檔也弄好了,有點像終於把魚骨頭挑出來一樣舒爽(・ωー)~☆

 

這是新的簽名檔喔

截圖 2021-04-25 上午1.24.13

 

圖是自己畫的XD

不過做出來效果比想像得好..畢竟我不是什麼設計系的囧

好啦,那本文就到這邊,最後..

TK

arrow
arrow

    碼農日常 發表在 痞客邦 留言(20) 人氣()