Jak utworzyć RecyclerView z wieloma typami widoków?

864

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

Podczas tworzenia RecyclerView.Adaptermusimy określić, ViewHolderże będzie się łączyć z adapterem.

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

Czy można tworzyć RecyclerViewz wieloma typami widoków?

Pongpat
źródło
5
Oprócz odpowiedzi Antona, zobacz moją odpowiedź tutaj: stackoverflow.com/questions/25914003/…
ticofab
Sprawdź te linki, które mogą Ci się przydać stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha
1
Dobry tutorial tutaj: guide.codepath.com/android/…
Gene Bo
Sprawdź ten link, czy można to zrobić stackoverflow.com/questions/39971350/... Jeśli jest
jakiś
Wielka biblioteka do implementacji github.com/vivchar/RendererRecyclerViewAdapter
Witalij

Odpowiedzi:

1268

Tak, to możliwe. Wystarczy zaimplementować getItemViewType () i zająć się viewTypeparametrem w onCreateViewHolder().

Więc robisz coś takiego:

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
źródło
3
Taki jest mój cel, ponieważ tylko jeden ViewHolder jest dostępny w jednym RecyclerView.Adapter, jak zamierzasz dodać do niego wiele?
Pongpat,
48
Następnie musisz rzutować typ widoku w metodzie onBindViewHolder (), która, jak sądzę, pokonuje cel typu ogólnego. Nawiasem mówiąc, dziękuję za odpowiedź.
Pongpat,
32
Możesz utworzyć BaseHolder i rozszerzyć go o wszystkie wymagane typy. Następnie dodaj abstrakcyjny setData, który zostałby zastąpiony (zastąpiony?) W posiadaczach implementacji. W ten sposób pozwalasz językowi radzić sobie z różnicami typów. Chociaż działa to tylko wtedy, gdy masz jeden zestaw danych, które wszystkie elementy listy mogą interpretować.
DariusL,
2
Co z innym plikiem układu? Chcę zmienić układ na optionMenuItem. Jak to możliwe? @AntonSavin
Pratik Butani
5
Twoje ViewHoldery powinny być nieruchome, jeśli znajdują się w adapterze RecyclerView
Samer
88

Jeśli układy typów widoków są tylko kilka, a logika wiązania jest prosta, zastosuj rozwiązanie Antona.
Ale kod będzie bałagan, jeśli musisz zarządzać złożonymi układami i logiką wiązania.

Uważam, że poniższe rozwiązanie przyda się komuś, kto musi obsługiwać złożone typy widoków.

Podstawowa klasa 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();

......

}

Funkcje potrzebne do zdefiniowania w tej klasie są prawie takie same jak klasa adaptera podczas tworzenia typu pojedynczego widoku.
Dla każdego typu widoku utwórz klasę, rozszerzając ten DataBinder.

Przykładowa klasa 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);
        }
    }
}

Aby zarządzać klasami DataBinder, utwórz klasę adaptera.

Podstawowa klasa 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);

......

}

Utwórz klasę, rozszerzając tę ​​klasę podstawową, a następnie utwórz instancję klas DataBinder i zastąp metody abstrakcyjne

  1. getItemCount
    Zwraca całkowitą liczbę elementów DataBinders

  2. getItemViewType
    Zdefiniuj logikę mapowania między pozycją adaptera a typem widoku.

  3. getDataBinder
    Zwraca instancję DataBinder na podstawie typu widoku

  4. getPosition
    Definiuje konwersję logiki do pozycji adaptera z pozycji w określonym DataBinder

  5. getBinderPosition
    Zdefiniuj konwersję logiki do pozycji w DataBinder z pozycji adaptera

Mam nadzieję, że to rozwiązanie będzie pomocne.
Zostawiłem bardziej szczegółowe rozwiązanie i próbki w GitHub, więc w razie potrzeby skorzystaj z poniższego linku.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter

yqritc
źródło
4
Jestem nieco zdezorientowany twoim kodem, być może możesz mi pomóc, nie chciałbym, aby moje widoki były definiowane przez pozycje na liście, ale przez ich typy widoków. Wygląda na to, że widoki w kodzie są określane na podstawie ich pozycji, tj. więc jeśli jestem w pozycji 1, wyświetlany jest widok 1, pozycja 3, widok 3 jest wyświetlany, a wszystko inne wyświetla widok pozycji 2. Nie chcę opierać moich widoków na pozycjach, ale na typach widoków - więc jeśli określę, że typem widoku są obrazy, powinien on wyświetlać obraz. Jak mogę to zrobić?
Simon
Niestety nie mogłem w pełni zrozumieć twojego pytania ..., ale musisz gdzieś napisać logikę, aby powiązać pozycję i typ widoku.
yqritc
1
ten kod nie jest mylony, jest to wzorzec adaptera RecyclerView i należy to wykluczyć, tak jak poprawną odpowiedź na pytanie. Kliknij link @yqritc, poświęć trochę czasu na odkrycie, a będziesz miał idealny wzór dla RecyclerView z różnymi typami układów.
Stoycho Andreev
newbe tutaj, public class DataBinder<T extends RecyclerView.ViewHolder>czy ktoś może mi powiedzieć, jak <T someClass>się nazywa, więc mogę google, jeśli dostanę termin. Także kiedy mówię, abstract public class DataBinder<T extends RecyclerView.ViewHolder>czy to oznacza, że ​​ta klasa jest typu ViewHolder, więc w rezultacie każda klasa, która ją rozszerza, będzie typu, viewHolderczy to jest pomysł?
rgv
1
@cesards kazałeś mi odświeżyć moją wiedzę na temat polimorfizmu lol .... Java nie jest zła
Paul Okeke
37

Poniższy kod nie jest pseudokodem, przetestowałem go i zadziałał dla mnie.

Chciałem utworzyć widok nagłówka w moim widoku recyklera, a następnie wyświetlić listę zdjęć poniżej nagłówka, którą użytkownik może kliknąć.

Użyłem kilku przełączników w moim kodzie, nie wiem, czy jest to najbardziej efektywny sposób, więc nie krępuj się i komentuj:

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

        }
    }
Szymon
źródło
To miłe, co jeśli chciałbym mieć wiele nagłówków na dynamicznych pozycjach? Powiedzmy listę pozycji z nagłówkami definiującymi kategorie. Wydaje się, że rozwiązanie wymaga, aby specjalne nagłówki znajdowały się we wcześniej określonych pozycjach int.
Bassinator
22

Tak to mozliwe.

Napisz ogólny uchwyt widoku:

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

    public abstract  void setDataOnView(int position);
}

następnie utwórz uchwyty widoków i zmuś je, aby rozszerzyły GenericViewHolder. Na przykład ten:

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

wtedy klasa RecyclerView.Adapter będzie wyglądać następująco:

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
źródło
Jak wykorzystać następnie w działaniu? Czy typ należy przekazać metodą?
skm
Jak korzystać z tego adaptera w działaniu? I jak rozpoznaje, który typ jest na liście
skm
20

Utwórz inny ViewHolder dla innego układu

wprowadź opis zdjęcia tutaj
RecyclerView może mieć dowolną liczbę wyświetlaczy, ale dla lepszej czytelności pozwala zobaczyć, jak utworzyć jeden z dwoma ViewHolderami.

Można to zrobić w trzech prostych krokach

  1. Nadpisanie public int getItemViewType(int position)
  2. Powrót różne ViewHolders oparciu o ViewType w onCreateViewHolder()metodzie
  3. Populate Zobacz na podstawie itemViewType w onBindViewHolder()metodzie

Oto mały fragment kodu

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 (pozycja wewnętrzna) jest kluczem

Moim zdaniem punktem wyjścia do stworzenia tego rodzaju recykleraView jest znajomość tej metody. Ponieważ ta metoda jest opcjonalna do zastąpienia, dlatego nie jest domyślnie widoczna w klasie RecylerView, co z kolei sprawia, że ​​wielu programistów (w tym ja) zastanawia się, od czego zacząć. Gdy dowiesz się, że ta metoda istnieje, utworzenie takiego RecyclerView byłoby bułką z masłem.

Zobaczmy jeden przykład, aby udowodnić mój punkt widzenia. Jeśli chcesz pokazać dwa układy na alternatywnych pozycjach, zrób to

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

Ważne linki:

Sprawdź projekt, w którym to zrealizowałem

Rohit Singh
źródło
15

Tak to mozliwe. W twoim układzie getItemViewType układ taki jak ten ....

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

dla odniesienia: https://www.journaldev.com/12372/android-recyclerview-example

Sayan Manna
źródło
Sformatowałem swój kod, aby naśladować ten fragment i teraz działa on doskonale. Problem, który miałem, polegał na tym, że po przesunięciu poza bieżącą stronę ulegnie awarii. Crash no More! Doskonały model. Dziękuję Ci . Dobra robota.
user462990,
Przez kilka dni nie mogłem znaleźć nic przydatnego, dziękuję!
Itay Braha
7

zgodnie z rozwiązaniem Antona, wymyśl to, ViewHolderktóre utrzymuje / obsługuje / deleguje różne typy układów. Ale nie jestem pewien, czy zastąpienie nowego układu będzie działać, gdy widok recyklingu ViewHoldernie jest rodzajem gromadzenia danych.

Zasadniczo onCreateViewHolder(ViewGroup parent, int viewType)jest wywoływany tylko wtedy, gdy potrzebny jest nowy układ widoku;

getItemViewType(int position)zostanie wezwany do viewType;

onBindViewHolder(ViewHolder holder, int position)jest zawsze wywoływany podczas recyklingu widoku (wprowadzane są nowe dane i próbuje się z nimi wyświetlić ViewHolder).

Kiedy więc onBindViewHoldersię nazywa, musi umieścić odpowiedni układ widoku i zaktualizować ViewHolder.

Czy sposób zastąpienia układu widoku jest poprawny ViewHolder, czy może jakiś problem? Doceń każdy komentarz!

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

Edycja: ViewHolder ma element mItemViewType do przechowywania widoku

Edycja: wygląda jak w onBindViewHolder (uchwyt ViewHolder, pozycja int) przekazany ViewHolder został odebrany (lub utworzony) przez sprawdzenie getItemViewType (pozycja int), aby upewnić się, że jest on zgodny, więc nie trzeba się martwić, że ViewHolder typ nie pasuje do typu danych [pozycja]. Czy ktoś wie więcej o tym, jak pobierane jest ViewHolder w onBindViewHolder ()?

Edycja: Wygląda na to, że Kosz ViewHolderjest wybierany według typu, więc nie ma tam wojownika.

Edycja: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ odpowiada na to pytanie.

Otrzymuje recykling ViewHolderjak:

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

lub utwórz nowy, jeśli nie znajdziesz recyklingu ViewHolderodpowiedniego typu.

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
źródło
6

Mam lepsze rozwiązanie, które pozwala tworzyć wiele typów widoków w sposób deklaratywny i bezpieczny dla typu. Jest napisany w Kotlinie, który jest naprawdę fajny.

Proste uchwyty widoków dla wszystkich wymaganych typów widoków

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
}

Istnieje abstrakcja elementu danych adaptera. Zauważ, że typ widoku jest reprezentowany przez hashCode określonej klasy posiadacza widoku (KClass w 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)
   }
}

Tylko bindViewHoldermusi być nadpisane w konkretnych klasach poz adapter (typ bezpieczny sposób)

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

Lista takich AdapterItemMediumobiektów jest źródłem danych dla adaptera, który faktycznie akceptuje, List<AdapterItem>patrz poniżej.

Ważną częścią tego rozwiązania jest fabryka uchwytów widoków, która zapewni nowe wystąpienia określonego ViewHolder

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

Prosta klasa adaptera wygląda następująco

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

Tylko 3 kroki, aby utworzyć nowy typ widoku:

  1. utwórz klasę widoku
  2. utwórz klasę elementu adaptera (rozszerzając z AdapterItemBase)
  3. zarejestruj klasę posiadacza widoku w ViewHolderProvider

Oto przykład tej koncepcji: android-szuflada-szablon To idzie jeszcze dalej - zobacz typ, który działa jak element spinnera, elementy adaptera do wyboru.

Michał Faber
źródło
6

To jest bardzo proste i proste.

Wystarczy zastąpić metodę getItemViewType () w adapterze. Na podstawie danych zwracane są różne wartości itemViewType. np. rozważ obiekt typu Person z członkiem isMale, jeśli isMale ma wartość true, zwróć 1, a isMale ma wartość false, zwróć 2 w metodzie getItemViewType () .

Teraz przychodzi do createViewHolder (nadrzędny ViewGroup, int viewType) , na podstawie różnych viewType, które można napompować inny plik układu. jak poniżej

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

w onBindViewHolder (uchwyt VH, pozycja int) sprawdź, gdzie uchwyt jest instancją AdapterFemaleViewHolderlub AdapterMaleViewHolderprzez instanceofi odpowiednio przypisz wartości.

ViewHolder Może tak być

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

    class AdapterFemaleViewHolder extends RecyclerView.ViewHolder {
         ...
         public AdapterFemaleViewHolder(View itemView){
            ...
         }
    }
Tulsi
źródło
4

Chociaż wybrana odpowiedź jest poprawna, chcę ją bardziej szczegółowo rozwinąć. Znalazłem tutaj przydatny adapter niestandardowy dla wielu typów widoków w RecyclerView . Jego wersja Kotlin jest tutaj .

Następuje niestandardowy adapter

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
źródło
3

Polecam tę bibliotekę od Hannesa Dorfmanna. Zawiera całą logikę związaną z danym typem widoku w oddzielnym obiekcie o nazwie „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
źródło
1

Właściwie chciałbym poprawić odpowiedź Antona .

Ponieważ getItemViewType(int position)zwraca wartość całkowitą, możesz zwrócić identyfikator zasobu układu, który musisz zawęzić. W ten sposób zaoszczędzisz trochę logiki w onCreateViewHolder(ViewGroup parent, int viewType)metodzie.

Ponadto nie sugerowałbym wykonywania intensywnych obliczeń, getItemCount()ponieważ ta konkretna funkcja jest wywoływana co najmniej 5 razy podczas renderowania listy, a także podczas renderowania każdego elementu poza elementy widoczne. Niestety, ponieważ notifyDatasetChanged()metoda jest ostateczna, tak naprawdę nie można jej przesłonić, ale można ją wywołać z innej funkcji w adapterze.

Dragas
źródło
3
Tak, to może zadziałać, ale może być mylące dla innych programistów. Również z dokumentacji Note: Integers must be in the range 0 to getViewTypeCount() - 1. IGNORE_ITEM_VIEW_TYPE can also be returned.Więc lepiej napisać nieco więcej kodu i nie używać hacków.
Ioane Sharvadze,
1
Zgadzam się. Wówczas brakowało mi tej konkretnej klauzuli.
Dragas,
To zabawne, ponieważ RecyclerView.Adapter: getItemViewType () dokumentuje tutaj developer.android.com/reference/android/support/v7/widget/... sugeruje, co napisał Dragas , że powinieneś „Rozważyć użycie zasobów identyfikatora do jednoznacznej identyfikacji typów widoków elementów. „ najwyraźniej nieświadomy wymogu getViewTypeCount ()
Deemoe
1

Możesz użyć biblioteki: 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 */

`

Dla każdego elementu należy zaimplementować ViewRenderer, ViewHolder, SomeModel:

ViewHolder - jest to prosty uchwyt widoku widoku recyklera.

SomeModel - to Twój model z ItemModelinterfejsem

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

Aby uzyskać więcej informacji, możesz przejrzeć dokumentację.

Witalij
źródło
1

Implementacja typów widoków staje się łatwiejsza dzięki kotlin, oto przykład z tą lekką biblioteką 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
        }
    }
}
Link182
źródło
1

Można sobie multipleViewTypes RecyclerAdapterdokonując getItemViewType()zwrotu oczekiwanej viewTypewartości dla tej pozycji

Przygotowałem MultipleViewTypeAdapterdo skonstruowania listy MCQ do egzaminów, które mogą zadać pytanie, które może zawierać 2 lub więcej poprawnych odpowiedzi (opcje pola wyboru) i pytania z jedną odpowiedzią (opcje przycisku radiowego).

W tym celu otrzymałem typ pytania z odpowiedzi API i użyłem go do podjęcia decyzji, który widok muszę pokazać dla tego pytania.

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

    }


}

Można uniknąć wielokrotnego wypełniania danych viewHolder opartych na warunkach, onBindViewHolder()przypisując te same identyfikatory do podobnych widoków w różnych viewHolderach, które różnią się położeniem.

Avinash Ch
źródło
0

Jeśli chcesz go używać w połączeniu z Android Data Binding, zajrzyj do https://github.com/evant/binding-collection-adapter - jest to zdecydowanie najlepsze rozwiązanie dla wielu typów widoków RecyclerView, jakie widziałem.

możesz go użyć jak

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

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

a następnie w układzie, w którym znajduje się lista

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

elementy listy muszą implementować BaseListIteminterfejs, który wygląda następująco

interface BaseListItem {
    val layoutRes: Int
}

a widok elementu powinien wyglądać mniej więcej tak

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

    <data>

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

   ...

</layout>

Gdzie YourListItemimplementujeBaseListItem

Mam nadzieję, że to komuś pomoże.

Pavlo Ostasha
źródło
0

najpierw musisz utworzyć 2 xml układu. po tym w środku recyclinglerview adapter TYPE_CALL i TYPE_EMAIL mają dwie wartości statyczne, odpowiednio 1 i 2 w klasie adaptera.

teraz Zdefiniuj dwie wartości statyczne na poziomie klasy Recycler, na przykład: private static int TYPE_CALL = 1; prywatny statyczny int TYPE_EMAIL = 2;

Teraz utwórz uchwyt widoku z wieloma takimi widokami:

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

Teraz koduj jak poniżej w metodach onCreateViewHolder i onBindViewHolder w adapterze recyclinglerview:

@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
źródło