Bagaimana cara membuat RecyclerView dengan beberapa tipe tampilan?

864

Dari https://developer.android.com/preview/material/ui-widgets.html

Ketika kita membuat RecyclerView.Adapterkita harus menentukan ViewHolderyang akan mengikat dengan adaptor.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
        public ViewHolder(TextView v) {
            super(v);
            mTextView = v;
        }
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false);

        //findViewById...

        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.mTextView.setText(mDataset[position]);
    }

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

Jadi, apakah mungkin membuat RecyclerViewdengan beberapa jenis tampilan?

Pongpat
sumber
5
Di atas jawaban Anton, lihat jawaban saya di sini: stackoverflow.com/questions/25914003/…
ticofab
Periksa tautan ini yang juga dapat berguna untuk Anda stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha
1
Tutorial yang bagus di sini: guides.codepath.com/android/…
Gene Bo
Periksa tautan ini apakah ini bisa digunakan stackoverflow.com/questions/39971350/... Jika ada masalah, tolong beri tahu saya
Ravindra Kushwaha
Pustaka Hebat untuk mengimplementasikannya github.com/vivchar/RendererRecyclerViewAdapter
Vitaly

Jawaban:

1269

Iya itu mungkin. Cukup terapkan getItemViewType () , dan urusviewType parameter di onCreateViewHolder().

Jadi Anda melakukan sesuatu seperti:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    class ViewHolder0 extends RecyclerView.ViewHolder {
        ...
        public ViewHolder0(View itemView){
        ...
        }
    }

    class ViewHolder2 extends RecyclerView.ViewHolder {
        ...
        public ViewHolder2(View itemView){
        ...
    }

    @Override
    public int getItemViewType(int position) {
        // Just as an example, return 0 or 2 depending on position
        // Note that unlike in ListView adapters, types don't have to be contiguous
        return position % 2 * 2;
    }

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

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        switch (holder.getItemViewType()) {
            case 0:
                ViewHolder0 viewHolder0 = (ViewHolder0)holder;
                ...
                break;

            case 2:
                ViewHolder2 viewHolder2 = (ViewHolder2)holder;
                ...
                break;
        }
    }
}
Anton Savin
sumber
3
Itu maksud saya karena hanya satu ViewHolder tersedia dalam satu RecyclerView.Adapter bagaimana Anda akan menambahkan beberapa ke dalamnya?
Pongpat
48
Maka Anda harus melemparkan tipe viewholder dalam metode onBindViewHolder () yang saya pikir itu mengalahkan tujuan tipe generik. Omong-omong, terima kasih atas jawaban Anda.
Pongpat
32
Anda dapat membuat BaseHolder dan memperluasnya untuk semua jenis yang diperlukan. Kemudian tambahkan setData abstrak, yang akan ditimpa (overrode?) Di pemegang implementasi. Dengan cara ini Anda membiarkan bahasa menangani perbedaan jenis. Meskipun hanya berfungsi jika Anda memiliki satu set data yang dapat ditafsirkan oleh semua item daftar.
DariusL
2
Bagaimana dengan file tata letak yang berbeda? Saya ingin mengubah tata letak optionMenuItem. Bagaimana mungkin? @AntonSavin
Pratik Butani
5
ViewHolders Anda seharusnya statis jika berada di RecyclerView Adapter Anda
Samer
88

Jika tata letak untuk jenis tampilan hanya sedikit dan logika pengikat sederhana, ikuti solusi Anton.
Tetapi kodenya akan berantakan jika Anda perlu mengatur tata letak yang rumit dan logika yang mengikat.

Saya percaya solusi berikut ini akan berguna bagi seseorang yang perlu menangani tipe tampilan yang kompleks.

Kelas Basis DataBinder

abstract public class DataBinder<T extends RecyclerView.ViewHolder> {

    private DataBindAdapter mDataBindAdapter;

    public DataBinder(DataBindAdapter dataBindAdapter) {
        mDataBindAdapter = dataBindAdapter;
    }

    abstract public T newViewHolder(ViewGroup parent);

    abstract public void bindViewHolder(T holder, int position);

    abstract public int getItemCount();

......

}

Fungsi yang diperlukan untuk mendefinisikan di kelas ini hampir sama dengan kelas adaptor saat membuat tipe tampilan tunggal.
Untuk setiap jenis tampilan, buat kelas dengan memperluas DataBinder ini.

Contoh kelas DataBinder

public class Sample1Binder extends DataBinder<Sample1Binder.ViewHolder> {

    private List<String> mDataSet = new ArrayList();

    public Sample1Binder(DataBindAdapter dataBindAdapter) {
        super(dataBindAdapter);
    }

    @Override
    public ViewHolder newViewHolder(ViewGroup parent) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
            R.layout.layout_sample1, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void bindViewHolder(ViewHolder holder, int position) {
        String title = mDataSet.get(position);
        holder.mTitleText.setText(title);
    }

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

    public void setDataSet(List<String> dataSet) {
        mDataSet.addAll(dataSet);
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        TextView mTitleText;

        public ViewHolder(View view) {
            super(view);
            mTitleText = (TextView) view.findViewById(R.id.title_type1);
        }
    }
}

Untuk mengelola kelas DataBinder, buat kelas adaptor.

Basiskan kelas DataBindAdapter

abstract public class DataBindAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return getDataBinder(viewType).newViewHolder(parent);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        int binderPosition = getBinderPosition(position);
        getDataBinder(viewHolder.getItemViewType()).bindViewHolder(viewHolder, binderPosition);
    }

    @Override
    public abstract int getItemCount();

    @Override
    public abstract int getItemViewType(int position);

    public abstract <T extends DataBinder> T getDataBinder(int viewType);

    public abstract int getPosition(DataBinder binder, int binderPosition);

    public abstract int getBinderPosition(int position);

......

}

Buat kelas dengan memperluas kelas dasar ini, dan kemudian instantiate kelas DataBinder dan timpa metode abstrak

  1. getItemCount
    Mengembalikan total jumlah item dari DataBinders

  2. getItemViewType
    Tentukan logika pemetaan antara posisi adaptor dan jenis tampilan.

  3. getDataBinder
    Mengembalikan instance DataBinder berdasarkan tipe tampilan

  4. getPosition
    Tentukan logika konversi ke posisi adaptor dari posisi di DataBinder yang ditentukan

  5. getBinderPosition
    Tentukan logika konversi ke posisi di DataBinder dari posisi adaptor

Semoga solusi ini bermanfaat.
Saya meninggalkan solusi dan sampel yang lebih detail di GitHub, jadi silakan merujuk tautan berikut jika Anda perlu.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter

yqritc
sumber
4
Saya sedikit bingung dengan kode Anda, mungkin Anda dapat membantu saya, saya tidak ingin pandangan saya ditentukan oleh posisi dalam daftar tetapi oleh tipe tampilan mereka. Tampaknya seolah-olah pandangan dalam kode Anda ditentukan berdasarkan posisi mereka yaitu. jadi jika saya pada posisi 1, tampilan 1 ditampilkan, posisi 3, tampilan 3 ditampilkan dan yang lainnya menampilkan tampilan posisi 2. Saya tidak ingin mendasarkan pandangan saya pada posisi tetapi pada tipe tampilan - jadi jika saya menentukan bahwa jenis tampilan adalah gambar, itu harus menampilkan gambar. Bagaimana saya bisa melakukan itu?
Simon
Maaf, saya tidak dapat sepenuhnya memahami pertanyaan Anda ..., tetapi Anda perlu menulis logika di suatu tempat untuk mengikat posisi dan jenis tampilan.
yqritc
1
kode ini tidak bingung, ini adalah pola Adaptor RecyclerView, dan ini harus dikecualikan seperti jawaban pertanyaan yang benar. Ikuti tautan @yqritc, habiskan sedikit waktu untuk menemukannya dan Anda akan memiliki pola yang sempurna untuk RecyclerView dengan berbagai jenis tata letak.
Stoycho Andreev
baru ada di sini, public class DataBinder<T extends RecyclerView.ViewHolder>bisakah seseorang memberi tahu saya apa <T someClass>yang disebut, jadi saya bisa google jika saya mendapatkan istilah. Juga ketika saya mengatakan abstract public class DataBinder<T extends RecyclerView.ViewHolder>apakah itu berarti bahwa kelas ini bertipe ViewHolder, jadi pada akhirnya setiap kelas yang memperluas kelas ini bertipe viewHolderapakah itu idenya?
rgv
1
@cesards Anda membuat saya untuk menyegarkan kembali pengetahuan saya tentang polimorfisme lol .... Java tidak buruk
Paul Okeke
37

Di bawah ini bukan pseudocode dan saya telah mengujinya dan telah bekerja untuk saya.

Saya ingin membuat headerview di recyclerview saya dan kemudian menampilkan daftar gambar di bawah header yang bisa diklik pengguna.

Saya menggunakan beberapa sakelar dalam kode saya, tidak tahu apakah itu cara paling efisien untuk melakukan ini, jadi jangan ragu untuk memberikan komentar Anda:

   public class ViewHolder extends RecyclerView.ViewHolder{

        //These are the general elements in the RecyclerView
        public TextView place;
        public ImageView pics;

        //This is the Header on the Recycler (viewType = 0)
        public TextView name, description;

        //This constructor would switch what to findViewBy according to the type of viewType
        public ViewHolder(View v, int viewType) {
            super(v);
            if (viewType == 0) {
                name = (TextView) v.findViewById(R.id.name);
                decsription = (TextView) v.findViewById(R.id.description);
            } else if (viewType == 1) {
                place = (TextView) v.findViewById(R.id.place);
                pics = (ImageView) v.findViewById(R.id.pics);
            }
        }
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent,
                                         int viewType)
    {
        View v;
        ViewHolder vh;
        // create a new view
        switch (viewType) {
            case 0: //This would be the header view in my Recycler
                v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.recyclerview_welcome, parent, false);
                vh = new ViewHolder(v,viewType);
                return  vh;
            default: //This would be the normal list with the pictures of the places in the world
                v = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.recyclerview_picture, parent, false);
                vh = new ViewHolder(v, viewType);
                v.setOnClickListener(new View.OnClickListener(){

                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(mContext, nextActivity.class);
                        intent.putExtra("ListNo",mRecyclerView.getChildPosition(v));
                        mContext.startActivity(intent);
                    }
                });
                return vh;
        }
    }

    //Overriden so that I can display custom rows in the recyclerview
    @Override
    public int getItemViewType(int position) {
        int viewType = 1; //Default is 1
        if (position == 0) viewType = 0; //if zero, it will be a header view
        return viewType;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //position == 0 means its the info header view on the Recycler
        if (position == 0) {
            holder.name.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,"name clicked", Toast.LENGTH_SHORT).show();
                }
            });
            holder.description.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,"description clicked", Toast.LENGTH_SHORT).show();
                }
            });
            //this means it is beyond the headerview now as it is no longer 0. For testing purposes, I'm alternating between two pics for now
        } else if (position > 0) {
           holder.place.setText(mDataset[position]);
            if (position % 2 == 0) {
               holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic1));
            }
            if (position % 2 == 1) {
                holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic2));
            }

        }
    }
Simon
sumber
Ini bagus, bagaimana jika saya ingin banyak header di posisi dinamis? Katakanlah, daftar item dengan header yang mendefinisikan kategori. Solusi Anda tampaknya membutuhkan header khusus untuk berada di posisi int yang telah ditentukan.
Bassinator
22

Ya itu mungkin.

Tulis pemegang tampilan umum:

    public abstract class GenericViewHolder extends RecyclerView.ViewHolder
{
    public GenericViewHolder(View itemView) {
        super(itemView);
    }

    public abstract  void setDataOnView(int position);
}

kemudian buat pemegang tampilan Anda dan buat mereka memperpanjang GenericViewHolder. Misalnya, ini:

     public class SectionViewHolder extends GenericViewHolder{
    public final View mView;
    public final TextView dividerTxtV;

    public SectionViewHolder(View itemView) {
        super(itemView);
        mView = itemView;
        dividerTxtV = (TextView) mView.findViewById(R.id.dividerTxtV);
    }

    @Override
    public void setDataOnView(int position) {
        try {
            String title= sections.get(position);
            if(title!= null)
                this.dividerTxtV.setText(title);
        }catch (Exception e){
            new CustomError("Error!"+e.getMessage(), null, false, null, e);
        }
    }
}

maka kelas RecyclerView.Adapter akan terlihat seperti ini:

public class MyClassRecyclerViewAdapter extends RecyclerView.Adapter<MyClassRecyclerViewAdapter.GenericViewHolder> {

@Override
public int getItemViewType(int position) {
     // depends on your problem
     switch (position) {
         case : return VIEW_TYPE1;
         case : return VIEW_TYPE2;
         ...
     }
}

    @Override
   public GenericViewHolder onCreateViewHolder(ViewGroup parent, int viewType)  {
    View view;
    if(viewType == VIEW_TYPE1){
        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout1, parent, false);
        return new SectionViewHolder(view);
    }else if( viewType == VIEW_TYPE2){
        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout2, parent, false);
        return new OtherViewHolder(view);
    }
    // Cont. other view holders ...
    return null;
   }

@Override
public void onBindViewHolder(GenericViewHolder holder, int position) {
    holder.setDataOnView(position);
}
Islam Assi
sumber
Bagaimana cara menggunakannya dalam suatu kegiatan? Haruskah jenis dilewatkan melalui metode ini?
skm
Bagaimana cara menggunakan Adaptor ini dalam Aktivitas? Dan bagaimana cara mengenali Tipe yang ada dalam daftar
skm
20

Buat ViewHolder yang berbeda untuk tata letak yang berbeda

masukkan deskripsi gambar di sini
RecyclerView dapat memiliki jumlah pemirsa yang Anda inginkan tetapi untuk keterbacaan yang lebih baik mari kita lihat cara membuat satu dengan dua ViewHolders.

Itu bisa dilakukan dalam tiga langkah sederhana

  1. Mengesampingkan public int getItemViewType(int position)
  2. Kembalikan ViewHolders berbeda berdasarkan onCreateViewHolder()metode ViewType dalam
  3. Populate View berdasarkan pada itemViewType dalam onBindViewHolder()metode

Berikut ini cuplikan kode kecil

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

getItemViewType (posisi int) adalah kuncinya

Menurut pendapat saya, titik awal untuk membuat jenis recyclerView ini adalah pengetahuan tentang metode ini. Karena metode ini opsional untuk ditimpa oleh karena itu tidak terlihat di kelas RecylerView secara default yang pada gilirannya membuat banyak pengembang (termasuk saya) bertanya-tanya harus mulai dari mana. Setelah Anda mengetahui bahwa metode ini ada, membuat RecyclerView seperti itu akan menjadi cakewalk.

Mari kita lihat satu contoh untuk membuktikan pendapat saya. Jika Anda ingin menampilkan dua tata letak pada posisi alternatif, lakukan ini

@Override
public int getItemViewType(int position)
{
   if(position%2==0)       // Even position 
     return LAYOUT_ONE;
   else                   // Odd position 
     return LAYOUT_TWO;
}

Tautan yang Relevan:

Lihatlah proyek tempat saya menerapkan ini

Rohit Singh
sumber
15

Ya itu mungkin. Di adaptor Anda, getItemViewType Layout seperti ini ....

  public class MultiViewTypeAdapter extends RecyclerView.Adapter {

        private ArrayList<Model>dataSet;
        Context mContext;
        int total_types;
        MediaPlayer mPlayer;
        private boolean fabStateVolume = false;

        public static class TextTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            CardView cardView;

            public TextTypeViewHolder(View itemView) {
                super(itemView);

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.cardView = (CardView) itemView.findViewById(R.id.card_view);
            }
        }

        public static class ImageTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            ImageView image;

            public ImageTypeViewHolder(View itemView) {
                super(itemView);

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.image = (ImageView) itemView.findViewById(R.id.background);
            }
        }

        public static class AudioTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            FloatingActionButton fab;

            public AudioTypeViewHolder(View itemView) {
                super(itemView);

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.fab = (FloatingActionButton) itemView.findViewById(R.id.fab);
            }
        }

        public MultiViewTypeAdapter(ArrayList<Model>data, Context context) {
            this.dataSet = data;
            this.mContext = context;
            total_types = dataSet.size();
        }

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

            View view;
            switch (viewType) {
                case Model.TEXT_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_type, parent, false);
                    return new TextTypeViewHolder(view);
                case Model.IMAGE_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_type, parent, false);
                    return new ImageTypeViewHolder(view);
                case Model.AUDIO_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.audio_type, parent, false);
                    return new AudioTypeViewHolder(view);
            }
            return null;
        }

        @Override
        public int getItemViewType(int position) {

            switch (dataSet.get(position).type) {
                case 0:
                    return Model.TEXT_TYPE;
                case 1:
                    return Model.IMAGE_TYPE;
                case 2:
                    return Model.AUDIO_TYPE;
                default:
                    return -1;
            }
        }

        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int listPosition) {

            Model object = dataSet.get(listPosition);
            if (object != null) {
                switch (object.type) {
                    case Model.TEXT_TYPE:
                        ((TextTypeViewHolder) holder).txtType.setText(object.text);

                        break;
                    case Model.IMAGE_TYPE:
                        ((ImageTypeViewHolder) holder).txtType.setText(object.text);
                        ((ImageTypeViewHolder) holder).image.setImageResource(object.data);
                        break;
                    case Model.AUDIO_TYPE:

                        ((AudioTypeViewHolder) holder).txtType.setText(object.text);

                }
            }
        }

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

untuk tautan referensi: https://www.journaldev.com/12372/android-recyclerview-example

Sayan Manna
sumber
Saya memformat ulang kode saya untuk meniru cuplikan ini dan sekarang berfungsi dengan baik. Masalah yang saya alami adalah bahwa menggesek di luar halaman saat ini akan macet. Kecelakaan Tidak Lagi! Model yang sangat baik ,. Terima kasih . Sudah selesai dilakukan dengan baik.
user462990
Tidak dapat menemukan sesuatu yang bermanfaat selama berhari-hari sampai saya melihat ini, Terima kasih!
Itay Braha
7

mengikuti solusi Anton, buat ini ViewHolderyang menampung / menangani / mendelegasikan berbagai jenis tata letak. Tetapi tidak yakin apakah mengganti tata letak baru akan berfungsi saat tampilan daur ulang ViewHolderbukan tipe data roll in.

Jadi pada dasarnya, onCreateViewHolder(ViewGroup parent, int viewType)hanya dipanggil ketika tata letak tampilan baru diperlukan;

getItemViewType(int position)akan dipanggil untuk viewType;

onBindViewHolder(ViewHolder holder, int position)selalu dipanggil saat mendaur ulang tampilan (data baru dibawa dan coba ditampilkan dengan itu ViewHolder).

Jadi ketika onBindViewHolderdipanggil perlu diletakkan di tata letak tampilan yang benar dan perbarui ViewHolder.

Apakah cara yang benar untuk mengganti tata letak tampilan untuk yang ViewHolderakan dibawa, atau masalah? Hargai komentar apa pun!

public int getItemViewType(int position) {
    TypedData data = mDataSource.get(position);
    return data.type;
}

public ViewHolder onCreateViewHolder(ViewGroup parent, 
    int viewType) {
    return ViewHolder.makeViewHolder(parent, viewType);
}

public void onBindViewHolder(ViewHolder holder, 
    int position) {
    TypedData data = mDataSource.get(position);
    holder.updateData(data);
}

///
public static class ViewHolder extends 
    RecyclerView.ViewHolder {

    ViewGroup mParentViewGroup;
    View mCurrentViewThisViewHolderIsFor;
    int mDataType;

    public TypeOneViewHolder mTypeOneViewHolder;
    public TypeTwoViewHolder mTypeTwoViewHolder;

    static ViewHolder makeViewHolder(ViewGroup vwGrp, 
        int dataType) {
        View v = getLayoutView(vwGrp, dataType);
        return new ViewHolder(vwGrp, v, viewType);
    }

    static View getLayoutView(ViewGroup vwGrp, 
        int dataType) {
        int layoutId = getLayoutId(dataType);
        return LayoutInflater.from(vwGrp.getContext())
                             .inflate(layoutId, null);
    }

    static int getLayoutId(int dataType) {
        if (dataType == TYPE_ONE) {
            return R.layout.type_one_layout;
        } else if (dataType == TYPE_TWO) {
            return R.layout.type_two_layout;
        }
    }

    public ViewHolder(ViewGroup vwGrp, View v, 
        int dataType) {
        super(v);
        mDataType = dataType;
        mParentViewGroup = vwGrp;
        mCurrentViewThisViewHolderIsFor = v;

        if (data.type == TYPE_ONE) {
            mTypeOneViewHolder = new TypeOneViewHolder(v);
        } else if (data.type == TYPE_TWO) {
            mTypeTwoViewHolder = new TypeTwoViewHolder(v);
        }
    }

    public void updateData(TypeData data) {
        mDataType = data.type;
        if (data.type == TYPE_ONE) {
            mTypeTwoViewHolder = null;
            if (mTypeOneViewHolder == null) {
                View newView = getLayoutView(mParentViewGroup,
                               data.type);

                /**
                 *  how to replace new view with 
                    the view in the parent 
                    view container ???
                 */
                replaceView(mCurrentViewThisViewHolderIsFor, 
                            newView);
                mCurrentViewThisViewHolderIsFor = newView;

                mTypeOneViewHolder = 
                    new TypeOneViewHolder(newView);
            }
            mTypeOneViewHolder.updateDataTypeOne(data);

        } else if (data.type == TYPE_TWO){
            mTypeOneViewHolder = null;
            if (mTypeTwoViewHolder == null) {
                View newView = getLayoutView(mParentViewGroup, 
                               data.type);

                /**
                 *  how to replace new view with 
                    the view in the parent view 
                    container ???
                 */
                replaceView(mCurrentViewThisViewHolderIsFor, 
                            newView);
                mCurrentViewThisViewHolderIsFor = newView;

                mTypeTwoViewHolder = 
                    new TypeTwoViewHolder(newView);
            }
            mTypeTwoViewHolder.updateDataTypeOne(data);
        }
    }
}

public static void replaceView(View currentView, 
    View newView) {
    ViewGroup parent = (ViewGroup)currentView.getParent();
    if(parent == null) {
        return;
    }
    final int index = parent.indexOfChild(currentView);
    parent.removeView(currentView);
    parent.addView(newView, index);
}

Sunting: ViewHolder memiliki anggota mItemViewType untuk menahan tampilan

Sunting: terlihat seperti pada onBindViewHolder (pemegang ViewHolder, posisi int) ViewHolder yang disahkan telah diambil (atau dibuat) dengan melihat getItemViewType (posisi int) untuk memastikan itu cocok, jadi mungkin tidak perlu khawatir di sana bahwa ViewHolder's tipe tidak cocok dengan tipe data [posisi]. Adakah yang tahu lebih banyak bagaimana ViewHolder di onBindViewHolder () diambil?

Edit: Sepertinya daur ulang ViewHolderdipilih berdasarkan jenis, jadi tidak ada prajurit di sana.

Edit: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ menjawab pertanyaan ini.

Mendapat daur ulang ViewHolder seperti:

holder = getRecycledViewPool().getRecycledView(mAdapter.getItemViewType(offsetPosition));

atau buat yang baru jika tidak menemukan daur ulang ViewHolderdari jenis yang tepat.

public ViewHolder getRecycledView(int viewType) {
        final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
        if (scrapHeap != null && !scrapHeap.isEmpty()) {
            final int index = scrapHeap.size() - 1;
            final ViewHolder scrap = scrapHeap.get(index);
            scrapHeap.remove(index);
            return scrap;
        }
        return null;
    }

View getViewForPosition(int position, boolean dryRun) {
    ......

    if (holder == null) {
            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
                        + "position " + position + "(offset:" + offsetPosition + ")."
                        + "state:" + mState.getItemCount());
            }

            final int type = mAdapter.getItemViewType(offsetPosition);
            // 2) Find from scrap via stable ids, if exists
            if (mAdapter.hasStableIds()) {
                holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
                if (holder != null) {
                    // update position
                    holder.mPosition = offsetPosition;
                    fromScrap = true;
                }
            }
            if (holder == null && mViewCacheExtension != null) {
                // We are NOT sending the offsetPosition because LayoutManager does not
                // know it.
                final View view = mViewCacheExtension
                        .getViewForPositionAndType(this, position, type);
                if (view != null) {
                    holder = getChildViewHolder(view);
                    if (holder == null) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned"
                                + " a view which does not have a ViewHolder");
                    } else if (holder.shouldIgnore()) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned"
                                + " a view that is ignored. You must call stopIgnoring before"
                                + " returning this view.");
                    }
                }
            }
            if (holder == null) { // fallback to recycler
                // try recycler.
                // Head to the shared pool.
                if (DEBUG) {
                    Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
                            + "pool");
                }
                holder = getRecycledViewPool()
                        .getRecycledView(mAdapter.getItemViewType(offsetPosition));
                if (holder != null) {
                    holder.resetInternal();
                    if (FORCE_INVALIDATE_DISPLAY_LIST) {
                        invalidateDisplayListInt(holder);
                    }
                }
            }
            if (holder == null) {
                holder = mAdapter.createViewHolder(RecyclerView.this,
                        mAdapter.getItemViewType(offsetPosition));
                if (DEBUG) {
                    Log.d(TAG, "getViewForPosition created new ViewHolder");
                }
            }
        }
        boolean bound = false;
        if (mState.isPreLayout() && holder.isBound()) {
            // do not update unless we absolutely have to.
            holder.mPreLayoutPosition = position;
        } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
            if (DEBUG && holder.isRemoved()) {
                throw new IllegalStateException("Removed holder should be bound and it should"
                        + " come here only in pre-layout. Holder: " + holder);
            }
            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
            mAdapter.bindViewHolder(holder, offsetPosition);
            attachAccessibilityDelegate(holder.itemView);
            bound = true;
            if (mState.isPreLayout()) {
                holder.mPreLayoutPosition = position;
            }
        }

        final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
        final LayoutParams rvLayoutParams;
        if (lp == null) {
            rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else if (!checkLayoutParams(lp)) {
            rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else {
            rvLayoutParams = (LayoutParams) lp;
        }
        rvLayoutParams.mViewHolder = holder;
        rvLayoutParams.mPendingInvalidate = fromScrap && bound;
        return holder.itemView;
}
lannyf
sumber
6

Saya memiliki solusi yang lebih baik yang memungkinkan untuk membuat beberapa tipe tampilan dengan cara deklaratif dan tipe aman. Itu ditulis dalam Kotlin yang btw sangat bagus.

Pemegang tampilan sederhana untuk semua jenis tampilan yang diperlukan

class ViewHolderMedium(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val icon: ImageView = itemView.findViewById(R.id.icon) as ImageView
    val label: TextView = itemView.findViewById(R.id.label) as TextView
}

Ada abstraksi dari item data adaptor. Perhatikan bahwa tipe tampilan diwakili oleh kode hash dari kelas pemegang tampilan tertentu (KClass di Kotlin)

trait AdapterItem {
   val viewType: Int
   fun bindViewHolder(viewHolder: RecyclerView.ViewHolder)
}

abstract class AdapterItemBase<T>(val viewHolderClass: KClass<T>) : AdapterItem {
   override val viewType: Int = viewHolderClass.hashCode()  
   abstract fun bindViewHolder(viewHolder: T)
   override fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) {
       bindViewHolder(viewHolder as T)
   }
}

Hanya bindViewHolderperlu diganti dalam kelas item adaptor beton (ketik cara aman)

class AdapterItemMedium(val icon: Drawable, val label: String, val onClick: () -> Unit) : AdapterItemBase<ViewHolderMedium>(ViewHolderMedium::class) {
    override fun bindViewHolder(viewHolder: ViewHolderMedium) {
        viewHolder.icon.setImageDrawable(icon)
        viewHolder.label.setText(label)
        viewHolder.itemView.setOnClickListener { onClick() }
    }
}

Daftar AdapterItemMediumobjek tersebut adalah sumber data untuk adaptor yang benar-benar menerima List<AdapterItem>lihat di bawah.

Bagian penting dari solusi ini adalah pabrik pemegang pandangan yang akan memberikan contoh baru dari ViewHolder tertentu

class ViewHolderProvider {
    private val viewHolderFactories = hashMapOf<Int, Pair<Int, Any>>()

    fun provideViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val (layoutId: Int, f: Any) = viewHolderFactories.get(viewType)
        val viewHolderFactory = f as (View) -> RecyclerView.ViewHolder
        val view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, viewGroup, false)
        return viewHolderFactory(view)
    }

    fun registerViewHolderFactory<T>(key: KClass<T>, layoutId: Int, viewHolderFactory: (View) -> T) {
        viewHolderFactories.put(key.hashCode(), Pair(layoutId, viewHolderFactory))
    }
}

Dan kelas adaptor sederhana terlihat seperti ini

public class MultitypeAdapter(val items: List<AdapterItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

   val viewHolderProvider = ViewHolderProvider() // inject ex Dagger2

   init {
        viewHolderProvider!!.registerViewHolderFactory(ViewHolderMedium::class, R.layout.item_medium, { itemView ->
            ViewHolderMedium(itemView)
        })
   }

   override fun getItemViewType(position: Int): Int {
        return items[position].viewType
    }

    override fun getItemCount(): Int {
        return items.size()
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder? {
        return viewHolderProvider!!.provideViewHolder(viewGroup, viewType)
    }

    override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
        items[position].bindViewHolder(viewHolder)     
    }
}

Hanya 3 langkah untuk membuat tipe tampilan baru:

  1. buat kelas pemegang pandangan
  2. buat kelas item adaptor (memanjang dari AdapterItemBase)
  3. daftar kelas pemegang tampilan di ViewHolderProvider

Berikut ini adalah contoh dari konsep ini: android-drawer-template Lebih jauh lagi - tipe tampilan yang bertindak sebagai komponen pemintal, item adaptor yang dapat dipilih.

Michal Faber
sumber
6

Ini sangat sederhana dan lurus ke depan.

Hanya Ganti metode getItemViewType () di adaptor Anda. Atas dasar data mengembalikan nilai itemViewType yang berbeda. mis. Pertimbangkan objek bertipe Person dengan anggota isMale, jika isMale benar, kembalikan 1 dan isMale salah, kembalikan 2 di getItemViewType () metode .

Sekarang sampai pada createViewHolder (induk ViewGroup, int viewType) , berdasarkan berbagai tipe tampilan Anda dapat mengembang file tata letak yang berbeda. seperti berikut ini

 if (viewType ==1){
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.male,parent,false);
    return new AdapterMaleViewHolder(view);
}
else{
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.female,parent,false);
    return new AdapterFemaleViewHolder(view);
}

di onBindViewHolder (pemegang VH, posisi int) periksa di mana pemegang adalah instance dari AdapterFemaleViewHolderatau AdapterMaleViewHolderoleh instanceofdan dengan demikian tetapkan nilainya.

ViewHolder Mungkin seperti ini

    class AdapterMaleViewHolder extends RecyclerView.ViewHolder {
            ...
            public AdapterMaleViewHolder(View itemView){
            ...
            }
        }

    class AdapterFemaleViewHolder extends RecyclerView.ViewHolder {
         ...
         public AdapterFemaleViewHolder(View itemView){
            ...
         }
    }
Tulsi
sumber
4

Meskipun jawaban yang dipilih benar, saya hanya ingin menjelaskannya lebih lanjut. Saya menemukan di sini Adaptor Kustom yang berguna untuk beberapa Jenis Tampilan di RecyclerView . Its versi Kotlin sini .

Adaptor Kustom berikut

public class CustomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context context;
ArrayList<String> list; // ArrayList of your Data Model
final int VIEW_TYPE_ONE = 1;
final int VIEW_TYPE_TWO = 2;

public CustomAdapter(Context context, ArrayList<String> list) { // you can pass other parameters in constructor
    this.context = context;
    this.list = list;
}

private class ViewHolder1 extends RecyclerView.ViewHolder {

    TextView yourView;
    ViewHolder1(final View itemView) {
        super(itemView);
        yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
    }
    void bind(int position) {
        // This method will be called anytime a list item is created or update its data
        //Do your stuff here
        yourView.setText(list.get(position));
    }
}

private class ViewHolder2 extends RecyclerView.ViewHolder {

    TextView yourView;
    ViewHolder2(final View itemView) {
        super(itemView);
        yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
    }
    void bind(int position) {
        // This method will be called anytime a list item is created or update its data
        //Do your stuff here
        yourView.setText(list.get(position));
    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   if (viewType == VIEW_TYPE_ONE) {
       return new ViewHolder1(LayoutInflater.from(context).inflate(R.layout.your_list_item_1, parent, false));
   }
   //if its not VIEW_TYPE_ONE then its VIEW_TYPE_TWO
   return new ViewHolder2(LayoutInflater.from(context).inflate(R.layout.your_list_item_2, parent, false));

}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (list.get(position).type == Something) { // put your condition, according to your requirements
        ((ViewHolder1) holder).bind(position);
    } else {
        ((ViewHolder2) holder).bind(position);
    }

}

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

@Override
public int getItemViewType(int position) {
    // here you can get decide from your model's ArrayList, which type of view you need to load. Like
    if (list.get(position).type == Something) { // put your condition, according to your requirements
        return VIEW_TYPE_ONE;
    }
    return VIEW_TYPE_TWO;
}
}
Asad Ali Choudhry
sumber
3

Saya merekomendasikan perpustakaan ini dari Hannes Dorfmann. Itu merangkum semua logika yang terkait dengan jenis tampilan tertentu dalam objek terpisah yang disebut "AdapterDelegate". https://github.com/sockeqwe/AdapterDelegates

public class CatAdapterDelegate extends AdapterDelegate<List<Animal>> {

  private LayoutInflater inflater;

  public CatAdapterDelegate(Activity activity) {
    inflater = activity.getLayoutInflater();
  }

  @Override public boolean isForViewType(@NonNull List<Animal> items, int position) {
    return items.get(position) instanceof Cat;
  }

  @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
    return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));
  }

  @Override public void onBindViewHolder(@NonNull List<Animal> items, int position,
      @NonNull RecyclerView.ViewHolder holder, @Nullable List<Object> payloads) {

    CatViewHolder vh = (CatViewHolder) holder;
    Cat cat = (Cat) items.get(position);

    vh.name.setText(cat.getName());
  }

  static class CatViewHolder extends RecyclerView.ViewHolder {

    public TextView name;

    public CatViewHolder(View itemView) {
      super(itemView);
      name = (TextView) itemView.findViewById(R.id.name);
    }
  }
}

public class AnimalAdapter extends ListDelegationAdapter<List<Animal>> {

  public AnimalAdapter(Activity activity, List<Animal> items) {

    // DelegatesManager is a protected Field in ListDelegationAdapter
    delegatesManager.addDelegate(new CatAdapterDelegate(activity))
                    .addDelegate(new DogAdapterDelegate(activity))
                    .addDelegate(new GeckoAdapterDelegate(activity))
                    .addDelegate(23, new SnakeAdapterDelegate(activity));

    // Set the items from super class.
    setItems(items);
  }
}
Dmitrii Bychkov
sumber
1

Sebenarnya, saya ingin memperbaiki jawaban Anton .

Karena getItemViewType(int position)mengembalikan nilai integer, Anda dapat mengembalikan ID sumber daya tata letak yang harus Anda kembangkan. Dengan begitu Anda akan menghemat beberapa logika dalam onCreateViewHolder(ViewGroup parent, int viewType)metode.

Juga, saya tidak akan menyarankan melakukan perhitungan intensif getItemCount()karena fungsi tertentu dipanggil setidaknya 5 kali saat merender daftar, serta saat merender setiap item di luar item yang terlihat. Sayangnya karena notifyDatasetChanged()metode ini final, Anda tidak dapat benar-benar menimpanya, tetapi Anda dapat memanggilnya dari fungsi lain di dalam adaptor.

Dragas
sumber
3
Ya, itu mungkin berhasil, tetapi akan membingungkan bagi pengembang lain. Juga dari Dokumentasi. Note: Integers must be in the range 0 to getViewTypeCount() - 1. IGNORE_ITEM_VIEW_TYPE can also be returned.Jadi lebih baik menulis sedikit lebih banyak kode dan tidak menggunakan peretasan.
Ioane Sharvadze
1
Saya setuju. Saat itu saya telah melewatkan klausa tertentu.
Dragas
Itu lucu karena RecyclerView.Adapter: getItemViewType () docs here developer.android.com/reference/android/support/v7/widget/… menyarankan apa yang diposting oleh Dragas bahwa Anda harus "Pertimbangkan menggunakan sumber daya id untuk secara unik mengidentifikasi jenis tampilan item. " tampaknya tidak menyadari persyaratan untuk getViewTypeCount ()
Deemoe
1

Anda dapat menggunakan perpustakaan: 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 */

`

Untuk setiap item, Anda harus menerapkan ViewRenderer, ViewHolder, SomeModel:

ViewHolder - ini adalah pemegang tampilan sederhana dari pendaur ulang.

SomeModel - ini adalah model Anda dengan ItemModelantarmuka

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));
 }
}

Untuk lebih jelasnya Anda bisa melihat dokumentasi.

Vitaly
sumber
1

Lihat jenis implementasi menjadi lebih mudah dengan kotlin, berikut adalah contoh dengan perpustakaan ringan ini https://github.com/Link184/KidAdapter

recyclerView.setUp {
    withViewType {
        withLayoutResId(R.layout.item_int)
        withItems(mutableListOf(1, 2, 3, 4, 5, 6))
        bind<Int> { // this - is adapter view hoder itemView, it - current item
            intName.text = it.toString()
        }
    }


    withViewType("SECOND_STRING_TAG") {
        withLayoutResId(R.layout.item_text)
        withItems(mutableListOf("eight", "nine", "ten", "eleven", "twelve"))
        bind<String> {
            stringName.text = it
        }
    }
}
Tautan182
sumber
1

Anda dapat menangani multipleViewTypes RecyclerAdapteroleh pembuatan getItemViewType()mengembalikan diharapkan viewTypenilai untuk posisi tersebut

Saya menyiapkan sebuah MultipleViewTypeAdapteruntuk menyusun daftar soal pilihan ganda untuk ujian yang dapat mengajukan pertanyaan yang mungkin memiliki 2 atau lebih jawaban yang valid (opsi kotak centang) dan satu pertanyaan jawaban tunggal (opsi radiobutton).

Untuk ini saya mendapatkan jenis Pertanyaan dari respons API dan saya menggunakannya untuk memutuskan tampilan mana yang harus saya tunjukkan untuk pertanyaan itu.

public class MultiViewTypeAdapter extends RecyclerView.Adapter {

    Context mContext;
    ArrayList<Question> dataSet;
    ArrayList<String> questions;
    private Object radiobuttontype1; 


    //Viewholder to display Questions with checkboxes
    public static class Checkboxtype2 extends RecyclerView.ViewHolder {
        ImageView imgclockcheck;
        CheckBox checkbox;

        public Checkboxtype2(@NonNull View itemView) {
            super(itemView);
            imgclockcheck = (ImageView) itemView.findViewById(R.id.clockout_cbox_image);
            checkbox = (CheckBox) itemView.findViewById(R.id.clockout_cbox);


        }
    }

        //Viewholder to display Questions with radiobuttons

    public static class Radiobuttontype1 extends RecyclerView.ViewHolder {
        ImageView clockout_imageradiobutton;
        RadioButton clockout_radiobutton;
        TextView sample;

        public radiobuttontype1(View itemView) {
            super(itemView);
            clockout_imageradiobutton = (ImageView) itemView.findViewById(R.id.clockout_imageradiobutton);
            clockout_radiobutton = (RadioButton) itemView.findViewById(R.id.clockout_radiobutton);
            sample = (TextView) itemView.findViewById(R.id.sample);
        }
    }

    public MultiViewTypeAdapter(ArrayList<QueDatum> data, Context context) {
        this.dataSet = data;
        this.mContext = context;

    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {

        if (viewType.equalsIgnoreCase("1")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("2")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_cbox_list_row, viewGroup, false);
            view.setHorizontalFadingEdgeEnabled(true);
            return new Checkboxtype2(view);

        } else if (viewType.equalsIgnoreCase("3")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("4")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("5")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);
        }


        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int viewType) {
        if (viewType.equalsIgnoreCase("1")) {
            options =  dataSet.get(i).getOptions();
            question = dataSet.get(i).getQuestion();
            image = options.get(i).getValue();
            ((radiobuttontype1) viewHolder).clockout_radiobutton.setChecked(false);
            ((radiobuttontype1) viewHolder).sample.setText(question);
            //loading image bitmap in the ViewHolder's View
            Picasso.with(mContext)
                    .load(image)
                    .into(((radiobuttontype1) viewHolder).clockout_imageradiobutton);

        } else if (viewType.equalsIgnoreCase("2")) {
            options = (ArrayList<Clockout_questions_Option>) dataSet.get(i).getOptions();
            question = dataSet.get(i).getQuestion();
            image = options.get(i).getValue();
            //loading image bitmap in the ViewHolder's View
            Picasso.with(mContext)
                    .load(image)
                    .into(((Checkboxtype2) viewHolder).imgclockcheck);

        } else if (viewType.equalsIgnoreCase("3")) {
                //fit data to viewHolder for ViewType 3
        } else if (viewType.equalsIgnoreCase("4")) {
//fit data to viewHolder for ViewType 4   
        } else if (viewType.equalsIgnoreCase("5")) {
//fit data to viewHolder for ViewType 5     
        }
    }

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

    /**
     * returns viewType for that position by picking the viewType value from the 
     *     dataset
     */
    @Override
    public int getItemViewType(int position) {
        return dataSet.get(position).getViewType();

    }


}

Anda dapat menghindari beberapa pengisian data viewHolder berbasis kondisional onBindViewHolder()dengan menetapkan id yang sama untuk tampilan serupa di viewHolder yang berbeda dalam penentuan posisi.

Avinash Ch
sumber
0

Jika Anda ingin menggunakannya bersamaan dengan tampilan Pengikatan Data Android ke https://github.com/evant/binding-collection-adapter - ini adalah solusi terbaik untuk berbagai jenis tampilan.RecyclerView bahkan pernah saya lihat.

Anda dapat menggunakannya seperti

var items: AsyncDiffPagedObservableList<BaseListItem> =
        AsyncDiffPagedObservableList(GenericDiff)

    val onItemBind: OnItemBind<BaseListItem> =
        OnItemBind { itemBinding, _, item -> itemBinding.set(BR.item, item.layoutRes) }

dan kemudian di tata letak di mana daftar berada

 <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                app:enableAnimations="@{false}"
                app:scrollToPosition="@{viewModel.scrollPosition}"

                app:itemBinding="@{viewModel.onItemBind}"
                app:items="@{viewModel.items}"

                app:reverseLayoutManager="@{true}"/>

item daftar Anda harus mengimplementasikan BaseListItemantarmuka yang terlihat seperti ini

interface BaseListItem {
    val layoutRes: Int
}

dan tampilan barang akan terlihat seperti ini

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
                name="item"
                type="...presentation.somescreen.list.YourListItem"/>
    </data>

   ...

</layout>

Di mana YourListItemmengimplementasikanBaseListItem

Semoga ini bisa membantu seseorang.

Pavlo Ostasha
sumber
0

pertama Anda harus membuat 2 layout xml. setelah itu di dalam adapter recyclerview, TYPE_CALL dan TYPE_EMAIL adalah dua nilai statis dengan masing-masing 1 dan 2 di kelas adaptor.

sekarang Tetapkan dua nilai statis pada tingkat kelas Adaptor pandangan Recycler misalnya: private static int TYPE_CALL = 1; private static int TYPE_EMAIL = 2;

Sekarang buat pemegang tampilan dengan banyak tampilan seperti ini:

class CallViewHolder extends RecyclerView.ViewHolder {

    private TextView txtName;
    private TextView txtAddress;

    CallViewHolder(@NonNull View itemView) {
        super(itemView);
        txtName = itemView.findViewById(R.id.txtName);
        txtAddress = itemView.findViewById(R.id.txtAddress);
    }
}
class EmailViewHolder extends RecyclerView.ViewHolder {

    private TextView txtName;
    private TextView txtAddress;

    EmailViewHolder(@NonNull View itemView) {
        super(itemView);
        txtName = itemView.findViewById(R.id.txtName);
        txtAddress = itemView.findViewById(R.id.txtAddress);
    }
}

Sekarang kode seperti di bawah ini di metode onCreateViewHolder dan onBindViewHolder di adaptor recyclerview:

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
    View view;
    if (viewType == TYPE_CALL) { // for call layout
        view = LayoutInflater.from(context).inflate(R.layout.item_call, viewGroup, false);
        return new CallViewHolder(view);

    } else { // for email layout
        view = LayoutInflater.from(context).inflate(R.layout.item_email, viewGroup, false);
        return new EmailViewHolder(view);
    }
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
    if (getItemViewType(position) == TYPE_CALL) {
        ((CallViewHolder) viewHolder).setCallDetails(employees.get(position));
    } else {
        ((EmailViewHolder) viewHolder).setEmailDetails(employees.get(position));
    }
}
Genius8020
sumber