Przegląd recyklingu i postępowanie z różnymi typami inflacji wierszowej

124

Próbuję pracować z nowym RecyclerView, ale nie mogłem znaleźć przykładu a RecyclerViewz różnymi typami wierszy / widoków kart, które są zawyżane.

Za pomocą ListViewnadpisuję getViewTypeCounti getItemViewType, aby obsługiwać różne typy wierszy.

Mam to zrobić w „stary” sposób, czy powinienem coś zrobić LayoutManager? Zastanawiałem się, czy ktoś mógłby wskazać mi właściwy kierunek. Ponieważ mogę znaleźć przykłady tylko z jednym typem.

Chcę mieć listę nieco innych kart. A może powinienem po prostu użyć scrollViewz cardViewswnętrzem ... zrobić to bez adaptera i recyclerView?

Lokkio
źródło
Jaka jest różnica między typami twoich przedmiotów? jak powinien reagować przegląd recyklingu na różne typy? Ogólnie rzecz biorąc, nie ma nic, co można zrobić z widokiem przewijania / widoku listy, czego nie można zrobić z widokiem recyklingu, ale nie na odwrót
Gil Moshay z
To tak, jak to, co widzisz w sklepie Google Play. U góry możesz mieć nagłówek, następnie możesz zobaczyć trzy karty, a następnie masz sekcję z informacjami. Czy odbywa się to w widoku recyklingu / widoku listy? Albo scrollview? Ponieważ jeśli jest to widok przewijania, muszę wcześniej określić wszystkie układy. Za pomocą widoku listy mogę po prostu dodać określone obiekty do mojego zbioru danych, a właściwy układ zostanie zawyżony. Więc chcę wiedzieć, jak wykonać ostatnią część z nowym Recyclerview, czy muszę zastąpić metody takie jak listview?
Lokkio
AnyOne szuka wersji demonstracyjnej na Github
nitesh
Sprawdź ten link, będzie działać dla Ciebie: - stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha

Odpowiedzi:

202

Obsługa logiki wierszy / sekcji podobna do UITableView systemu iOS nie jest tak prosta w systemie Android, jak w systemie iOS, jednak w przypadku korzystania z RecyclerView - elastyczność tego, co możesz zrobić, jest znacznie większa.

Ostatecznie chodzi o to, jak dowiedzieć się, jaki typ widoku wyświetlasz w adapterze. Kiedy już to zrozumiesz, żeglowanie powinno być łatwe (nie do końca, ale przynajmniej to załatwisz).

Adapter udostępnia dwie metody, które należy ominąć:

getItemViewType(int position)

Domyślna implementacja tej metody zawsze zwróci 0, co oznacza, że ​​istnieje tylko 1 typ widoku. W twoim przypadku tak nie jest, więc będziesz musiał znaleźć sposób, aby stwierdzić, który wiersz odpowiada danemu typowi widoku. W przeciwieństwie do iOS, który zarządza tym za Ciebie za pomocą wierszy i sekcji, tutaj będziesz mieć tylko jeden indeks, na którym możesz polegać, i będziesz musiał wykorzystać swoje umiejętności programisty, aby wiedzieć, kiedy pozycja jest skorelowana z nagłówkiem sekcji, a kiedy jest skorelowana z normalny rząd.

createViewHolder(ViewGroup parent, int viewType)

I tak musisz zastąpić tę metodę, ale zwykle ludzie po prostu ignorują parametr viewType. Zgodnie z typem widoku musisz nadmuchać odpowiedni zasób układu i odpowiednio utworzyć uchwyt widoku. RecyclerView będzie obsługiwać recykling różnych typów widoków w sposób, który pozwala uniknąć kolizji różnych typów widoków.

Jeśli planujesz używać domyślnego LayoutManagera, takiego jak LinearLayoutManager, powinieneś być gotowy . Jeśli planujesz stworzyć własną implementację LayoutManager, będziesz musiał popracować trochę ciężej. Jedynym API, z którym naprawdę musisz pracować, jest to, findViewByPosition(int position)które daje dany widok w określonej pozycji. Ponieważ prawdopodobnie będziesz chciał to ułożyć inaczej w zależności od typu tego widoku, masz kilka opcji:

  1. Zwykle, gdy używasz wzorca ViewHolder, ustawiasz etykietę widoku za pomocą uchwytu widoku. Możesz użyć tego w czasie wykonywania w menedżerze układu, aby dowiedzieć się, jakiego typu jest widok, dodając pole w uchwycie widoku, które to wyraża.

  2. Ponieważ będziesz potrzebować funkcji, która określa, która pozycja odpowiada typowi widoku, równie dobrze możesz w jakiś sposób uczynić tę metodę globalnie dostępną (może pojedynczą klasą zarządzającą danymi?), A następnie możesz po prostu zapytać o tę samą metodę zgodnie z pozycja.

Oto przykładowy kod:

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

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

public class MyAdapter extends Adapter<ViewHolder> {

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

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


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

        final int itemType = getItemViewType(position);

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

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

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

Oto próbka tego, jak powinny wyglądać te osoby posiadające widoki:

public MyHeaderViewHolder extends ViewHolder {

    private TextView headerLabel;    

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

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

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


public MyNormalViewHolder extends ViewHolder {

    private TextView titleLabel;
    private TextView descriptionLabel;    

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

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

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

Oczywiście w tym przykładzie założono, że źródło danych (myData) zostało utworzone w sposób ułatwiający implementację adaptera w ten sposób. Jako przykład pokażę, jak skonstruowałbym źródło danych, które pokazuje listę nazwisk i nagłówek za każdym razem, gdy zmienia się pierwsza litera nazwiska (zakładając, że lista jest alfabetyczna) - podobnie jak w przypadku kontaktów lista wyglądałaby następująco:

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

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

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

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

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

    myData = data.toArray();
}

Ten przykład ma na celu rozwiązanie dość konkretnego problemu, ale mam nadzieję, że daje to dobry przegląd sposobów obsługi różnych typów wierszy w recyklerze i pozwala na dokonanie niezbędnych dostosowań we własnym kodzie do własnych potrzeb.

Gil Moshayof
źródło
Całkiem super i myślę, że to świetny przykład do rozwiązania takiego problemu Przykładowe rozwiązanie
Amt87
Kolejny przykład infalowania różnych wierszy w recyelview, czyli: - stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha
Powinno byćnames[i].substring(0, 1)
Kyle
1
Ponadto w przypadku widoków recyklera z elementami heterogenicznymi warto również przyjrzeć się SpanSizeLookup. stackoverflow.com/questions/26869312/…
Mahori
To jest użyteczne. Opierając się na tej odpowiedzi, mam również pomysł, aby zaimplementować widok wielu typów w adapterze przy użyciu wyliczenia. Wyliczenie będzie miało metodę, onCreateViewHolderktóra pomoże nam stworzyć uchwyt widoku. Aby uzyskać więcej informacji, możesz sprawdzić mój udział: stackoverflow.com/questions/47245398/ ...
quangson91
114

Sztuczka polega na utworzeniu podklas ViewHolder, a następnie ich rzuceniu.

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

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

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

A następnie w czasie wykonywania zrób coś takiego:

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

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

    switch (viewHolder.getItemViewType()) {

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

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

Mam nadzieję, że to pomoże.

ticofab
źródło
3
To jest bezpośrednia odpowiedź na pytanie. Jedyną brakującą częścią jest potrzeba zastąpienia onCreateViewHolder (element nadrzędny ViewGroup, int viewType) i obsługi różnych typów widoków w oparciu o viewType
user1928896
Kolejny przykład infalowania różnych wierszy w recyelview, czyli: - stackoverflow.com/questions/39971350/…
Ravindra Kushwaha
1
Czy istnieje jakieś ogólne rozwiązanie zamiast rzutowania podstawy uchwytu widoku na wartości obudowy przełącznika?
Vahid Ghadiri
33

Według Gila świetna odpowiedź, którą rozwiązałem, zastępując getItemViewType, jak wyjaśnił Gil. Jego odpowiedź jest świetna i należy ją oznaczyć jako poprawną. W każdym razie dodaję kod, aby osiągnąć wynik:

W adapterze do recyklingu:

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

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

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

W ten sposób możesz ustawić dowolny niestandardowy układ dla dowolnego wiersza!

iGio90
źródło
18
Tylko drobny komentarz: drugim parametrem w onCreateViewHolder powinien być typ widoku, a nie indeks. Według API: developer.android.com/reference/android/support/v7/widget/… , int)
Mark Martinsson
ale co z tym, że użytkownik szybko przewija w tym momencie jakieś dziwne wyniki, które otrzymuję.
Rjz Satvara
15

Jest to dość trudne, ale o wiele trudne, po prostu skopiuj poniższy kod i gotowe

package com.yuvi.sample.main;

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


import com.yuvi.sample.R;

import java.util.List;

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

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

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

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

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

    private void setUpProfileView(ProfileViewHolder mholder) {

    }

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




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

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

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

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


}

cieszyć się !!!!

yubaraj poudel
źródło
Mój Viewholder1 ma układ o nazwie myLaout1.xml i zawiera ScrollView. Więc teraz, kiedy przewijam to, przewijany jest widok recyklingu. Jak przewijać zawartość
Viewholder1
3

Możemy uzyskać wiele widoków na jednym RecyclerView od dołu: -

Zależności w Gradle, więc dodaj poniższy kod: -

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

RecyclerView w XML

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

Kod działania

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

Pierwszy XML

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

Drugi XML

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

Trzeci XML

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

Teraz czas na zrobienie adaptera, a to jest główne do pokazania różnych widoków -2 w tym samym widoku recyklera, więc sprawdź dokładnie ten kod: -

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

Możesz również sprawdzić to łącze, aby uzyskać więcej informacji.

duggu
źródło
ale to działa dobrze, ale kiedy przewijam szybko od góry do dołu i odwrotnie, otrzymuję dziwne dane wyjściowe ... oznacza, że ​​dane nie są ustawione prawidłowo. jakie jest jego rozwiązanie?
Rjz Satvara
2

Musisz zaimplementować getItemViewType()metodę w RecyclerView.Adapter. Domyślnie onCreateViewHolder(ViewGroup parent, int viewType)implementacja viewTypetej metody zwraca 0. Po pierwsze, musisz zobaczyć typ przedmiotu na pozycji, aby wyświetlić recykling, a do tego musisz nadpisać getItemViewType()metodę, w której możesz przejść, viewTypektóra zwróci twoją pozycję przedmiotu. Przykład kodu podano poniżej

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

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

// and in the similar way you can set data according 
// to view holder position by passing position in getItemViewType
@Override
public void onBindViewHolder(MyViewholder viewholder, int position) {
    int listViewItemType = getItemViewType(position);
    // ...
}
Amandeep Rohila
źródło
2

getItemViewType (int position) jest kluczem

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

wprowadź opis obrazu tutaj

Jak to zrobić ?

Możesz utworzyć RecyclerViewz dowolną liczbą różnych widoków (ViewHolders). Ale dla lepszej czytelności weźmy przykład RecyclerViewz dwoma Viewholders.
Zapamiętaj te 3 proste kroki, a będziesz gotowy do pracy.

  • Zastąp publiczne wew getItemViewType(int position)
  • Zwróć różne ViewHolders na podstawie metody ViewTypeonCreateViewHolder ()
  • Populate Zobacz na podstawie itemViewType w onBindViewHolder()metodzie

    Oto fragment kodu dla Ciebie

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

Kod GitHub:

Oto projekt, w którym zaimplementowałem RecyclerView z wieloma ViewHolders.

Rohit Singh
źródło
A co z tym samym, ale z wieloma zestawami danych?
esQmo_
Co masz na myśli? @esQmo_
Rohit Singh
Co mam na myśli, jeśli każdy vieholder ma również inny zbiór danych (źródło danych)?
esQmo_
1

Możesz po prostu zwrócić ItemViewType i użyć go. Zobacz poniższy kod:

@Override
public int getItemViewType(int position) {

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

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    View view = LayoutInflater.from(viewGroup.getContext()).inflate(viewType, viewGroup, false);
    return new ViewHolder(view);
}
김영호
źródło
1

Możesz skorzystać z 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 recyklera.

SomeModel - to Twój model z ItemModel interfejsem

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 zajrzeć do dokumentacji.

Witalij
źródło
0

Możesz skorzystać z tej biblioteki:
https://github.com/kmfish/MultiTypeListViewAdapter (napisana przeze mnie)

  • Lepiej ponownie użyj kodu jednej komórki
  • Lepsza ekspansja
  • Lepsze odsprzęganie

Adapter konfiguracji:

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

Dla każdego elementu zamówienia:

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

    TextView tvName;
    TextView tvDesc;


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

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

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

    }

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

    public interface OnItem1ClickListener {
        void onNameClick(TextModel model);
        void onDescClick(TextModel model);
    }
}
kmfish
źródło