การรีไซเคิลและการจัดการอัตราเงินเฟ้อของแถวประเภทต่างๆ


124

ฉันพยายามทำงานกับสิ่งใหม่RecyclerViewแต่ไม่พบตัวอย่างRecyclerViewของแถว / มุมมองการ์ดประเภทต่างๆที่สูงเกินจริง

เมื่อListViewฉันแทนที่getViewTypeCountและgetItemViewTypeสำหรับการจัดการแถวประเภทต่างๆ

ฉันควรจะทำเหมือนวิธี "เก่า" หรือฉันควรทำอะไรกับLayoutManager? ฉันสงสัยว่าจะมีใครชี้ทางที่ถูกต้องให้ฉันได้ไหม เพราะฉันสามารถหาตัวอย่างได้เพียงประเภทเดียว

ฉันต้องการมีรายชื่อการ์ดที่แตกต่างกันเล็กน้อย ฉันควรหรือเพียงแค่ใช้scrollViewกับcardViewsภายในของมัน ... ให้มันได้โดยไม่ต้องอะแดปเตอร์และrecyclerView?


ประเภทรายการของคุณแตกต่างกันอย่างไร? การดูรีไซเคิลควรตอบสนองต่อประเภทต่างๆอย่างไร โดยทั่วไปไม่มีอะไรที่คุณสามารถทำได้ด้วย scrollview / listview ซึ่งคุณไม่สามารถทำได้ด้วยมุมมองรีไซเคิล แต่ไม่ใช่วิธีอื่น
Gil Moshayof

มันเหมือนกับสิ่งที่คุณเห็นใน Google Play Store จริงๆ ที่ด้านบนคุณสามารถมีส่วนหัวจากนั้นคุณจะเห็นไพ่สามใบจากนั้นคุณจะมีส่วนที่มีข้อมูล สิ่งนี้ทำในมุมมองรีไซเคิล / listview หรือไม่ หรือ scrollview? เพราะถ้ามันเป็นมุมมองแบบเลื่อนฉันต้องกำหนดเลย์เอาต์ทั้งหมดก่อน ด้วยมุมมองรายการฉันสามารถเพิ่มวัตถุบางอย่างลงในชุดข้อมูลของฉันและเค้าโครงที่ถูกต้องจะสูงเกินจริง ดังนั้นฉันต้องการทราบวิธีการทำส่วนสุดท้ายด้วย Recyclerview ใหม่ฉันต้องแทนที่วิธีการเช่น listview หรือไม่
Lokkio

AnyOne กำลังมองหาการสาธิต github สำหรับการจัดวางหลายแถวโดยใช้รหัสรีไซเคิล2concept.blogspot.in/2015/10/…
nitesh

อาจซ้ำกันได้ของHow to create RecyclerView with multiple view type?
Ashok Varma

ตรวจสอบลิงก์เหล่านี้จะสามารถใช้งานได้สำหรับคุณ: - stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha

คำตอบ:


203

การจัดการตรรกะของแถว / ส่วนที่คล้ายกับ UITableView ของ iOS นั้นไม่ง่ายอย่างใน Android เหมือนกับใน iOS อย่างไรก็ตามเมื่อคุณใช้ RecyclerView ความยืดหยุ่นของสิ่งที่คุณทำได้นั้นยิ่งใหญ่กว่ามาก

ในตอนท้ายทั้งหมดนี้เป็นข้อมูลเกี่ยวกับวิธีที่คุณทราบว่าคุณกำลังแสดงมุมมองประเภทใดในอะแดปเตอร์ เมื่อคุณเข้าใจแล้วก็ควรจะแล่นเรือได้ง่าย (ไม่จริง แต่อย่างน้อยคุณก็จะจัดเรียงได้)

อะแดปเตอร์แสดงวิธีการสองวิธีที่คุณควรแทนที่:

getItemViewType(int position)

การใช้งานเริ่มต้นของวิธีนี้จะคืนค่า 0 เสมอแสดงว่ามีมุมมองเพียง 1 ประเภท ในกรณีของคุณไม่เป็นเช่นนั้นดังนั้นคุณจะต้องหาวิธียืนยันว่าแถวใดตรงกับประเภทมุมมองใด ซึ่งแตกต่างจาก iOS ซึ่งจัดการสิ่งนี้ให้คุณด้วยแถวและส่วนที่นี่คุณจะมีดัชนีเพียงรายการเดียวที่ต้องพึ่งพาและคุณจะต้องใช้ทักษะของนักพัฒนาเพื่อที่จะทราบว่าตำแหน่งใดสัมพันธ์กับส่วนหัวของส่วนและเมื่อใดที่สัมพันธ์กับ แถวปกติ

createViewHolder(ViewGroup parent, int viewType)

คุณต้องแทนที่วิธีนี้อยู่ดี แต่โดยปกติแล้วผู้คนจะเพิกเฉยต่อพารามิเตอร์ viewType ตามประเภทมุมมองคุณจะต้องขยายทรัพยากรการจัดวางที่ถูกต้องและสร้างตัวยึดมุมมองของคุณตามนั้น RecyclerView จะจัดการการรีไซเคิลมุมมองประเภทต่างๆเพื่อหลีกเลี่ยงการปะทะกันของมุมมองประเภทต่างๆ

หากคุณกำลังวางแผนที่จะใช้ LayoutManager เริ่มต้นเช่นLinearLayoutManagerคุณควรจะไป หากคุณกำลังวางแผนที่จะใช้ LayoutManager ของคุณเองคุณจะต้องทำงานให้หนักขึ้นเล็กน้อย API เดียวที่คุณต้องใช้จริงๆคือfindViewByPosition(int position)ให้มุมมองที่กำหนดในตำแหน่งหนึ่ง ๆ เนื่องจากคุณอาจต้องการจัดวางให้แตกต่างกันไปขึ้นอยู่กับประเภทของมุมมองนี้คุณจึงมีตัวเลือกดังนี้

  1. โดยปกติเมื่อใช้รูปแบบ ViewHolder คุณจะตั้งค่าแท็กของมุมมองด้วยตัวยึดมุมมอง คุณสามารถใช้สิ่งนี้ระหว่างรันไทม์ในตัวจัดการโครงร่างเพื่อค้นหาว่ามุมมองเป็นประเภทใดโดยการเพิ่มฟิลด์ในตัวยึดมุมมองที่แสดงสิ่งนี้

  2. เนื่องจากคุณจะต้องมีฟังก์ชันที่กำหนดว่าตำแหน่งใดสัมพันธ์กับประเภทมุมมองใดคุณจึงอาจทำให้วิธีนี้สามารถเข้าถึงได้ทั่วโลก (อาจเป็นคลาสเดี่ยวที่จัดการข้อมูล?) จากนั้นคุณสามารถสืบค้นวิธีการเดียวกันตาม ตำแหน่ง.

นี่คือตัวอย่างโค้ด:

// in this sample, I use an object array to simulate the data of the list. 
// I assume that if the object is a String, it means I should display a header with a basic title.
// If not, I assume it's a custom model object I created which I will use to bind my normal rows.
private Object[] myData;

public static final int ITEM_TYPE_NORMAL = 0;
public static final int ITEM_TYPE_HEADER = 1;

public class MyAdapter extends Adapter<ViewHolder> {

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        if (viewType == ITEM_TYPE_NORMAL) {
            View normalView = LayoutInflater.from(getContext()).inflate(R.layout.my_normal_row, null);
            return new MyNormalViewHolder(normalView); // view holder for normal items
        } else if (viewType == ITEM_TYPE_HEADER) {
            View headerRow = LayoutInflater.from(getContext()).inflate(R.layout.my_header_row, null);
            return new MyHeaderViewHolder(headerRow); // view holder for header items
        }
    }


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

        final int itemType = getItemViewType(position);

        if (itemType == ITEM_TYPE_NORMAL) {
            ((MyNormalViewHolder)holder).bindData((MyModel)myData[position]);
        } else if (itemType == ITEM_TYPE_HEADER) {
            ((MyHeaderViewHolder)holder).setHeaderText((String)myData[position]);
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (myData[position] instanceof String) {
            return ITEM_TYPE_HEADER;
        } else {
            return ITEM_TYPE_NORMAL;
        }
    }

    @Override
    public int getItemCount() {
        return myData.length;
    }
}

นี่คือตัวอย่างลักษณะของผู้ถือมุมมองเหล่านี้:

public MyHeaderViewHolder extends ViewHolder {

    private TextView headerLabel;    

    public MyHeaderViewHolder(View view) {
        super(view);

        headerLabel = (TextView)view.findViewById(R.id.headerLabel);
    }

    public void setHeaderText(String text) {
        headerLabel.setText(text);
    }    
}


public MyNormalViewHolder extends ViewHolder {

    private TextView titleLabel;
    private TextView descriptionLabel;    

    public MyNormalViewHolder(View view) {
        super(view);

        titleLabel = (TextView)view.findViewById(R.id.titleLabel);
        descriptionLabel = (TextView)view.findViewById(R.id.descriptionLabel);
    }

    public void bindData(MyModel model) {
        titleLabel.setText(model.getTitle());
        descriptionLabel.setText(model.getDescription());
    }    
}

แน่นอนว่าตัวอย่างนี้ถือว่าคุณได้สร้างแหล่งข้อมูล (myData) ของคุณด้วยวิธีที่ทำให้ง่ายต่อการใช้งานอะแดปเตอร์ด้วยวิธีนี้ ตัวอย่างเช่นฉันจะแสดงให้คุณเห็นว่าฉันสร้างแหล่งข้อมูลอย่างไรซึ่งจะแสดงรายชื่อและส่วนหัวทุกครั้งที่ตัวอักษรตัวที่ 1 ของชื่อเปลี่ยนไป (สมมติว่ารายการเรียงตามตัวอักษร) - คล้ายกับที่ติดต่อ รายการจะมีลักษณะดังนี้:

// Assume names & descriptions are non-null and have the same length.
// Assume names are alphabetized
private void processDataSource(String[] names, String[] descriptions) {
    String nextFirstLetter = "";
    String currentFirstLetter;

    List<Object> data = new ArrayList<Object>();

    for (int i = 0; i < names.length; i++) {
        currentFirstLetter = names[i].substring(0, 1); // get the 1st letter of the name

        // if the first letter of this name is different from the last one, add a header row
        if (!currentFirstLetter.equals(nextFirstLetter)) {
            nextFirstLetter = currentFirstLetter;
            data.add(nextFirstLetter);
        }

        data.add(new MyModel(names[i], descriptions[i]));
    }

    myData = data.toArray();
}

ตัวอย่างนี้มาเพื่อแก้ปัญหาที่ค่อนข้างเฉพาะเจาะจง แต่ฉันหวังว่านี่จะช่วยให้คุณเห็นภาพรวมที่ดีเกี่ยวกับวิธีจัดการแถวประเภทต่างๆในเครื่องรีไซเคิลและช่วยให้คุณทำการปรับเปลี่ยนที่จำเป็นในรหัสของคุณเองเพื่อให้เหมาะกับความต้องการของคุณ



อีกตัวอย่างหนึ่งสำหรับการแทรกแถวต่างๆภายใน recyelview นั่นคือ: - stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha

ควรจะเป็นnames[i].substring(0, 1)
Kyle

1
นอกจากนี้สำหรับมุมมองของผู้รีไซเคิลที่มีรายการที่แตกต่างกันคุณควรดู SpanSizeLookup ด้วย stackoverflow.com/questions/26869312/…
Mahori

มันมีประโยชน์ จากคำตอบนี้ฉันมีความคิดที่จะใช้มุมมองหลายประเภทในอะแดปเตอร์โดยใช้ enum enum จะมีวิธีการonCreateViewHolderที่ช่วยให้เราสร้างผู้ถือมุมมอง สำหรับรายละเอียดเพิ่มเติมคุณอาจต้องการตรวจสอบการแบ่งปันของฉัน: stackoverflow.com/questions/47245398/…
quangson91

114

เคล็ดลับคือการสร้างคลาสย่อยของ ViewHolder จากนั้นจึงแคสต์

public class GroupViewHolder extends RecyclerView.ViewHolder {
    TextView mTitle;
    TextView mContent;
    public GroupViewHolder(View itemView) {
        super (itemView);
        // init views...
    }
}

public class ImageViewHolder extends RecyclerView.ViewHolder {
    ImageView mImage;
    public ImageViewHolder(View itemView) {
        super (itemView);
        // init views...
    }
}

private static final int TYPE_IMAGE = 1;
private static final int TYPE_GROUP = 2;  

จากนั้นในรันไทม์ทำสิ่งนี้:

@Override
public int getItemViewType(int position) {
    // here your custom logic to choose the view type
    return position == 0 ? TYPE_IMAGE : TYPE_GROUP;
}

@Override
public void onBindViewHolder (ViewHolder viewHolder, int i) {

    switch (viewHolder.getItemViewType()) {

        case TYPE_IMAGE:
            ImageViewHolder imageViewHolder = (ImageViewHolder) viewHolder;
            imageViewHolder.mImage.setImageResource(...);
            break;

        case TYPE_GROUP:
            GroupViewHolder groupViewHolder = (GroupViewHolder) viewHolder;
            groupViewHolder.mContent.setText(...)
            groupViewHolder.mTitle.setText(...);
            break;
    }
}

หวังว่าจะช่วยได้


3
นี่คือคำตอบโดยตรงสำหรับคำถาม ส่วนที่ขาดหายไปเพียงอย่างเดียวคือความจำเป็นในการแทนที่ onCreateViewHolder (ViewGroup parent, int viewType) และจัดการประเภทมุมมองที่แตกต่างกันตาม viewType
user1928896

อีกตัวอย่างหนึ่งสำหรับการแทรกแถวต่างๆภายใน recyelview นั่นคือ: - stackoverflow.com/questions/39971350/…
Ravindra Kushwaha

1
มีวิธีแก้ปัญหาทั่วไปแทนการแคสต์ตัวยึดมุมมองตามค่าเคสสวิตช์หรือไม่?
Vahid Ghadiri

33

ตามคำตอบที่ยอดเยี่ยมของ Gil ฉันแก้ไขได้โดยการแทนที่ getItemViewType ตามที่ Gil อธิบาย คำตอบของเขาดีมากและต้องทำเครื่องหมายว่าถูกต้อง ไม่ว่าในกรณีใดฉันเพิ่มรหัสเพื่อให้ได้คะแนน:

ในอะแดปเตอร์รีไซเคิลของคุณ:

@Override
public int getItemViewType(int position) {
    int viewType = 0;
    // add here your booleans or switch() to set viewType at your needed
    // I.E if (position == 0) viewType = 1; etc. etc.
    return viewType;
}

@Override
public FileViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == 0) {
        return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_layout_for_first_row, parent, false));
    }

    return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_other_rows, parent, false));
}

ด้วยการทำเช่นนี้คุณสามารถกำหนดรูปแบบที่กำหนดเองสำหรับแถวใดก็ได้!


18
เป็นเพียงความคิดเห็นเล็กน้อย: พารามิเตอร์ที่สองใน onCreateViewHolder ควรเป็น viewType ไม่ใช่ดัชนี อ้างอิงจาก API: developer.android.com/reference/android/support/v7/widget/… , int)
Mark Martinsson

แต่สิ่งที่เกี่ยวกับเมื่อผู้ใช้เลื่อนอย่างรวดเร็วในขณะนั้นผลลัพธ์แปลก ๆ ที่ฉันได้รับ
Rjz Satvara

15

มันค่อนข้างยุ่งยาก แต่ก็ยากมากเพียงแค่คัดลอกโค้ดด้านล่างเท่านี้ก็เสร็จแล้ว

package com.yuvi.sample.main;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;


import com.yuvi.sample.R;

import java.util.List;

/**
 * Created by yubraj on 6/17/15.
 */

public class NavDrawerAdapter extends RecyclerView.Adapter<NavDrawerAdapter.MainViewHolder> {
    List<MainOption> mainOptionlist;
    Context context;
    private static final int TYPE_PROFILE = 1;
    private static final int TYPE_OPTION_MENU = 2;
    private int selectedPos = 0;
    public NavDrawerAdapter(Context context){
        this.mainOptionlist = MainOption.getDrawableDataList();
        this.context = context;
    }

    @Override
    public int getItemViewType(int position) {
        return (position == 0? TYPE_PROFILE : TYPE_OPTION_MENU);
    }

    @Override
    public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType){
            case TYPE_PROFILE:
                return new ProfileViewHolder(LayoutInflater.from(context).inflate(R.layout.row_profile, parent, false));
            case TYPE_OPTION_MENU:
                return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.row_nav_drawer, parent, false));
        }
        return null;
    }

    @Override
    public void onBindViewHolder(MainViewHolder holder, int position) {
        if(holder.getItemViewType() == TYPE_PROFILE){
            ProfileViewHolder mholder = (ProfileViewHolder) holder;
            setUpProfileView(mholder);
        }
        else {
            MyViewHolder mHolder = (MyViewHolder) holder;
            MainOption mo = mainOptionlist.get(position);
            mHolder.tv_title.setText(mo.title);
            mHolder.iv_icon.setImageResource(mo.icon);
            mHolder.itemView.setSelected(selectedPos == position);
        }
    }

    private void setUpProfileView(ProfileViewHolder mholder) {

    }

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




public class MyViewHolder extends MainViewHolder{
    TextView tv_title;
    ImageView iv_icon;

    public MyViewHolder(View v){
        super(v);
        this.tv_title = (TextView) v.findViewById(R.id.tv_title);
        this.iv_icon = (ImageView) v.findViewById(R.id.iv_icon);
        v.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Redraw the old selection and the new
                notifyItemChanged(selectedPos);
                selectedPos = getLayoutPosition();
                notifyItemChanged(selectedPos);
            }
        });
    }
}
    public class ProfileViewHolder extends MainViewHolder{
        TextView tv_name, login;
        ImageView iv_profile;

        public ProfileViewHolder(View v){
            super(v);
            this.tv_name = (TextView) v.findViewById(R.id.tv_profile);
            this.iv_profile = (ImageView) v.findViewById(R.id.iv_profile);
            this.login = (TextView) v.findViewById(R.id.tv_login);
        }
    }

    public void trace(String tag, String message){
        Log.d(tag , message);
    }
    public class MainViewHolder extends  RecyclerView.ViewHolder {
        public MainViewHolder(View v) {
            super(v);
        }
    }


}

สนุก !!!!


My Viewholder1 มีเค้าโครงชื่อ myLaout1.xml และมี ScrollView อยู่ในนั้น ตอนนี้เมื่อฉันเลื่อนสิ่งนี้มุมมองรีไซเคิลจะเลื่อน วิธีการเลื่อนเนื้อหาของ
Viewholder1

3

เราสามารถบรรลุหลายมุมมองใน RecyclerView เดียวจากวิธีด้านล่าง: -

การพึ่งพา Gradle ดังนั้นให้เพิ่มโค้ดด้านล่าง: -

compile 'com.android.support:cardview-v7:23.0.1'
compile 'com.android.support:recyclerview-v7:23.0.1'

RecyclerView ใน XML

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

รหัสกิจกรรม

private RecyclerView mRecyclerView;
private CustomAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private String[] mDataset = {“Data - one ”, Data - two”,
    Showing data three”, Showing data four”};
private int mDatasetTypes[] = {DataOne, DataTwo, DataThree}; //view types
 
...
 
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mLayoutManager = new LinearLayoutManager(MainActivity.this);
mRecyclerView.setLayoutManager(mLayoutManager);
//Adapter is created in the last step
mAdapter = new CustomAdapter(mDataset, mDataSetTypes);
mRecyclerView.setAdapter(mAdapter);

XML แรก

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/ten"
    android:elevation="@dimen/hundered”
    card_view:cardBackgroundColor=“@color/black“>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding=“@dimen/ten">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=“Fisrt”
            android:textColor=“@color/white“ />
 
        <TextView
            android:id="@+id/temp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/ten"
            android:textColor="@color/white"
            android:textSize="30sp" />
    </LinearLayout>
 
</android.support.v7.widget.CardView>

XML ที่สอง

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/ten"
    android:elevation="100dp"
    card_view:cardBackgroundColor="#00bcd4">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="@dimen/ten">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=“DataTwo”
            android:textColor="@color/white" />
 
        <TextView
            android:id="@+id/score"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/ten"
            android:textColor="#ffffff"
            android:textSize="30sp" />
    </LinearLayout>
 
</android.support.v7.widget.CardView>

XML ที่สาม

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/ten"
    android:elevation="100dp"
    card_view:cardBackgroundColor="@color/white">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="@dimen/ten">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=“DataThree” />
 
        <TextView
            android:id="@+id/headline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/ten"
            android:textSize="25sp" />
 
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/ten"
            android:id="@+id/read_more"
            android:background="@color/white"
            android:text=“Show More/>
    </LinearLayout>
 
</android.support.v7.widget.CardView>

ตอนนี้ถึงเวลาสร้างอะแดปเตอร์แล้วและนี่เป็นสิ่งสำคัญสำหรับการแสดง -2 มุมมองที่แตกต่างกันในมุมมองรีไซเคิลเดียวกันดังนั้นโปรดตรวจสอบการโฟกัสของโค้ดนี้ให้ครบถ้วน: -

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    private static final String TAG = "CustomAdapter";
 
    private String[] mDataSet;
    private int[] mDataSetTypes;
 
    public static final int dataOne = 0;
    public static final int dataTwo = 1;
    public static final int dataThree = 2;
 
 
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View v) {
            super(v);
        }
    }
 
    public class DataOne extends ViewHolder {
        TextView temp;
 
        public DataOne(View v) {
            super(v);
            this.temp = (TextView) v.findViewById(R.id.temp);
        }
    }
 
    public class DataTwo extends ViewHolder {
        TextView score;
 
        public DataTwo(View v) {
            super(v);
            this.score = (TextView) v.findViewById(R.id.score);
        }
    }
 
    public class DataThree extends ViewHolder {
        TextView headline;
        Button read_more;
 
        public DataThree(View v) {
            super(v);
            this.headline = (TextView) v.findViewById(R.id.headline);
            this.read_more = (Button) v.findViewById(R.id.read_more);
        }
    }
 
 
    public CustomAdapter(String[] dataSet, int[] dataSetTypes) {
        mDataSet = dataSet;
        mDataSetTypes = dataSetTypes;
    }
 
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View v;
        if (viewType == dataOne) {
            v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.weather_card, viewGroup, false);
 
            return new DataOne(v);
        } else if (viewType == dataTwo) {
            v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.news_card, viewGroup, false);
            return new DataThree(v);
        } else {
            v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.score_card, viewGroup, false);
            return new DataTwo(v);
        }
    }
 
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        if (viewHolder.getItemViewType() == dataOne) {
            DataOne holder = (DataOne) viewHolder;
            holder.temp.setText(mDataSet[position]);
        }
        else if (viewHolder.getItemViewType() == dataTwo) {
            DataThree holder = (DataTwo) viewHolder;
            holder.headline.setText(mDataSet[position]);
        }
        else {
            DataTwo holder = (DataTwo) viewHolder;
            holder.score.setText(mDataSet[position]);
        }
    }
 
    @Override
    public int getItemCount() {
        return mDataSet.length;
    }
 
   @Override
    public int getItemViewType(int position) {
        return mDataSetTypes[position];
    }
}

คุณสามารถตรวจสอบลิงค์นี้เพื่อดูข้อมูลเพิ่มเติม


แต่มันก็ใช้งานได้ดี แต่เมื่อฉันเลื่อนอย่างรวดเร็วจากบนลงล่างและในทางกลับกันฉันได้ผลลัพธ์แปลก ๆ ... หมายความว่าข้อมูลไม่ได้ตั้งค่าให้เหมาะสม ทางออกของมันคืออะไร?
Rjz Satvara

2

คุณต้องใช้getItemViewType()วิธีการในRecyclerView.Adapter. โดยค่าเริ่มต้นonCreateViewHolder(ViewGroup parent, int viewType)การดำเนินการของวิธีการนี้ผลตอบแทนviewType 0ประการแรกคุณต้องมีประเภทการดูของรายการที่ตำแหน่งเพื่อวัตถุประสงค์ในการรีไซเคิลมุมมองและเพื่อที่คุณจะต้องแทนที่getItemViewType()วิธีการที่คุณสามารถส่งผ่านviewTypeซึ่งจะส่งคืนตำแหน่งของรายการของคุณ ตัวอย่างโค้ดได้รับด้านล่าง

@Override
public MyViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
    int listViewItemType = getItemViewType(viewType);
    switch (listViewItemType) {
         case 0: return new ViewHolder0(...);
         case 2: return new ViewHolder2(...);
    }
}

@Override
public int getItemViewType(int position) {   
    return position;
}

// and in the similar way you can set data according 
// to view holder position by passing position in getItemViewType
@Override
public void onBindViewHolder(MyViewholder viewholder, int position) {
    int listViewItemType = getItemViewType(position);
    // ...
}

2

getItemViewType (ตำแหน่ง int) เป็นกุญแจสำคัญ

ในความคิดของฉันจุดเริ่มต้นในการสร้าง RecyclerView ประเภทนี้คือความรู้เกี่ยวกับวิธีนี้ เนื่องจากวิธีนี้เป็นทางเลือกในการลบล้างดังนั้นจึงไม่สามารถมองเห็นได้ในคลาส RecylerView ตามค่าเริ่มต้นซึ่งจะทำให้นักพัฒนาหลายคน (รวมถึงฉัน) สงสัยว่าจะเริ่มจากตรงไหน เมื่อคุณรู้ว่ามีวิธีนี้อยู่การสร้าง RecyclerView ดังกล่าวจะเป็นทางเดินเค้ก

ใส่คำอธิบายภาพที่นี่

ทำอย่างไร ?

คุณสามารถสร้างRecyclerViewด้วยมุมมองที่แตกต่างกันจำนวนเท่าใดก็ได้ (ViewHolders) แต่เพื่อความสามารถในการอ่านที่ดีขึ้นลองใช้ตัวอย่างRecyclerViewสองViewholdersข้อ
จำ3ขั้นตอนง่ายๆนี้ไว้แล้วคุณจะไปได้ด้วยดี

  • ลบล้าง int สาธารณะ getItemViewType(int position)
  • ส่งคืน ViewHolders ที่แตกต่างกันตามวิธีViewTypeonCreateViewHolder ()
  • เติมมุมมองตาม itemViewType ในonBindViewHolder()วิธีการ

    นี่คือข้อมูลโค้ดสำหรับคุณ

    public class YourListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        private static final int LAYOUT_ONE= 0;
        private static final int LAYOUT_TWO= 1;
    
        @Override
        public int getItemViewType(int position)
        {
            if(position==0)
               return LAYOUT_ONE;
            else
               return LAYOUT_TWO;
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    
            View view =null;
            RecyclerView.ViewHolder viewHolder = null;
    
            if(viewType==LAYOUT_ONE)
            {
               view = LayoutInflater.from(parent.getContext()).inflate(R.layout.one,parent,false);
               viewHolder = new ViewHolderOne(view);
            }
            else
            {
               view = LayoutInflater.from(parent.getContext()).inflate(R.layout.two,parent,false);
               viewHolder= new ViewHolderTwo(view);
            }
    
            return viewHolder;
        }
    
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
    
           if(holder.getItemViewType()== LAYOUT_ONE)
           {
               // Typecast Viewholder 
               // Set Viewholder properties 
               // Add any click listener if any 
           }
           else {
    
               ViewHolderOne vaultItemHolder = (ViewHolderOne) holder;
               vaultItemHolder.name.setText(displayText);
               vaultItemHolder.name.setOnClickListener(new View.OnClickListener() {
                   @Override
                   public void onClick(View v) {
                       .......
                   }
               });
    
           }
    
       }
    
       /****************  VIEW HOLDER 1 ******************//
    
       public class ViewHolderOne extends RecyclerView.ViewHolder {
    
           public TextView name;
    
           public ViewHolderOne(View itemView) {
           super(itemView);
           name = (TextView)itemView.findViewById(R.id.displayName);
           }
       }
    
    
      //****************  VIEW HOLDER 2 ******************//
    
      public class ViewHolderTwo extends RecyclerView.ViewHolder{
    
           public ViewHolderTwo(View itemView) {
           super(itemView);
    
               ..... Do something
           }
      }
    }

รหัส GitHub:

นี่คือโครงการที่ฉันใช้ RecyclerView กับ ViewHolders หลายตัว


สิ่งที่เหมือนกัน แต่มีชุดข้อมูลหลายชุด?
esQmo_

คุณหมายถึงอะไร? @esQmo_
Rohit Singh

ฉันหมายความว่าอย่างไรถ้าผู้ถือหุ้นทุกรายมีชุดข้อมูลที่แตกต่างกัน (แหล่งข้อมูล)
esQmo_

1

คุณสามารถส่งคืน ItemViewType และใช้งานได้ ดูรหัสด้านล่าง:

@Override
public int getItemViewType(int position) {

    Message item = messageList.get(position);
    // return my message layout
    if(item.getUsername() == Message.userEnum.I)
        return R.layout.item_message_me;
    else
        return R.layout.item_message; // return other message layout
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    View view = LayoutInflater.from(viewGroup.getContext()).inflate(viewType, viewGroup, false);
    return new ViewHolder(view);
}

1

คุณสามารถใช้ไลบรารี: https://github.com/vivchar/RendererRecyclerViewAdapter

mRecyclerViewAdapter = new RendererRecyclerViewAdapter(); /* included from library */
mRecyclerViewAdapter.registerRenderer(new SomeViewRenderer(SomeModel.TYPE, this));
mRecyclerViewAdapter.registerRenderer(...); /* you can use several types of cells */

สำหรับแต่ละรายการคุณควรใช้ ViewRenderer, ViewHolder, SomeModel:

ViewHolder - เป็นผู้ถือมุมมองที่เรียบง่ายของมุมมองรีไซเคิล

SomeModel - เป็นโมเดลของคุณที่มีItemModelอินเทอร์เฟซ

public class SomeViewRenderer extends ViewRenderer<SomeModel, SomeViewHolder> {

    public SomeViewRenderer(final int type, final Context context) {
        super(type, context);
    }

    @Override
    public void bindView(@NonNull final SomeModel model, @NonNull final SomeViewHolder holder) {
       holder.mTitle.setText(model.getTitle());
    }

    @NonNull
    @Override
    public SomeViewHolder createViewHolder(@Nullable final ViewGroup parent) {
        return new SomeViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.some_item, parent, false));
    }
}

สำหรับรายละเอียดเพิ่มเติมคุณสามารถดูเอกสาร


0

คุณสามารถใช้ไลบรารีนี้ได้:
https://github.com/kmfish/MultiTypeListViewAdapter (เขียนโดยฉัน)

  • นำรหัสของเซลล์เดียวกลับมาใช้ใหม่ได้ดีขึ้น
  • การขยายตัวที่ดีขึ้น
  • การแยกส่วนที่ดีกว่า

ติดตั้งอะแดปเตอร์:

adapter = new BaseRecyclerAdapter();
adapter.registerDataAndItem(TextModel.class, LineListItem1.class);
adapter.registerDataAndItem(ImageModel.class, LineListItem2.class);
adapter.registerDataAndItem(AbsModel.class, AbsLineItem.class);

สำหรับแต่ละรายการโฆษณา:

public class LineListItem1 extends BaseListItem<TextModel, LineListItem1.OnItem1ClickListener> {

    TextView tvName;
    TextView tvDesc;


    @Override
    public int onGetLayoutRes() {
        return R.layout.list_item1;
    }

    @Override
    public void bindViews(View convertView) {
        Log.d("item1", "bindViews:" + convertView);
        tvName = (TextView) convertView.findViewById(R.id.text_name);
        tvDesc = (TextView) convertView.findViewById(R.id.text_desc);

        tvName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != attachInfo) {
                    attachInfo.onNameClick(getData());
                }
            }
        });
        tvDesc.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != attachInfo) {
                    attachInfo.onDescClick(getData());
                }
            }
        });

    }

    @Override
    public void updateView(TextModel model, int pos) {
        if (null != model) {
            Log.d("item1", "updateView model:" + model + "pos:" + pos);
            tvName.setText(model.getName());
            tvDesc.setText(model.getDesc());
        }
    }

    public interface OnItem1ClickListener {
        void onNameClick(TextModel model);
        void onDescClick(TextModel model);
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.