Recyclerview und Umgang mit verschiedenen Arten von Reihen Inflation

Ich versuche, mit dem neuen RecyclerView , aber ich konnte kein Beispiel für einen RecyclerView bei dem verschiedene Arten von Reihen / Kartenansichten aufgeblasen werden.

Mit ListView überschreibe ich die getViewTypeCount und getItemViewType , um verschiedene Arten von Zeilen zu behandeln.

Soll ich das “alt” machen oder sollte ich etwas mit LayoutManager ? Ich habe mich gefragt, ob mir jemand in die richtige Richtung zeigen könnte. Weil ich nur Beispiele mit einem Typ finden kann.

Ich möchte eine Liste leicht unterschiedlicher Karten haben. Oder sollte ich einfach eine scrollView mit cardViews darin verwenden … machen Sie es ohne den Adapter und recyclerView ?

Die Verarbeitung der Zeilen / Sektions-Logik ist ähnlich wie bei iOS UITableView in Android nicht so einfach wie in iOS, wenn Sie jedoch RecyclerView verwenden – die Flexibilität dessen, was Sie tun können, ist weitaus größer.

Am Ende geht es darum, wie Sie herausfinden, welche Art von Ansicht Sie im Adapter anzeigen. Sobald du das herausgefunden hast, sollte es einfach zu segeln sein (nicht wirklich, aber zumindest wirst du das sortiert haben).

Der Adapter macht zwei Methoden verfügbar, die Sie überschreiben sollten:

 getItemViewType(int position) 

Die Standardimplementierung dieser Methode gibt immer 0 zurück, um anzuzeigen, dass nur ein Ansichtstyp vorhanden ist. In Ihrem Fall ist dies nicht der Fall und Sie müssen einen Weg finden, um zu bestätigen, welche Zeile welchem ​​Ansichtstyp entspricht. Im Gegensatz zu iOS, das dies für Sie mit Zeilen und Abschnitten verwaltet, haben Sie hier nur einen Index, auf den Sie sich verlassen können. Sie müssen Ihre Entwicklerfähigkeiten verwenden, um zu wissen, wann eine Position mit einer Abschnittsüberschrift korreliert eine normale Reihe.

 createViewHolder(ViewGroup parent, int viewType) 

Sie müssen diese Methode trotzdem überschreiben, aber normalerweise ignorieren Benutzer einfach den Parameter viewType. Je nach Ansichtstyp müssen Sie die korrekte Layout-Ressource aufblasen und Ihren Ansichtshalter entsprechend erstellen. Der RecyclerView wird verschiedene Ansichtsarten auf eine Art und Weise recyceln, die das Aufeinanderprallen verschiedener Ansichtsarten vermeidet.

Wenn Sie einen standardmäßigen LayoutManager wie LinearLayoutManager , sollten Sie gut gehen. Wenn Sie eine eigene LayoutManager-Implementierung planen, müssen Sie etwas härter arbeiten. Die einzige API, mit der Sie wirklich arbeiten müssen, ist findViewByPosition(int position) die eine bestimmte Sicht auf eine bestimmte Position gibt. Da Sie es wahrscheinlich anders darstellen möchten, je nachdem, welcher Typ diese Ansicht ist, haben Sie einige Optionen:

  1. Wenn Sie das ViewHolder-Muster verwenden, legen Sie das Tag der Ansicht normalerweise mit dem Ansichtshalter fest. Sie können dies während der Laufzeit im Layout-Manager verwenden, um herauszufinden, um welchen Typ es sich bei der Ansicht handelt, indem Sie ein Feld im Ansichtshalter hinzufügen, das dies ausdrückt.

  2. Da Sie eine function benötigen, die bestimmt, welche Position zu welchem ​​Ansichtstyp gehört, können Sie diese Methode auch global zugänglich machen (vielleicht eine Singleton-class, die die Daten verwaltet?), Und dann können Sie einfach die gleiche Methode nach die Position.

Hier ist ein Codebeispiel:

 // 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 { @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; } } 

Hier ist ein Beispiel, wie diese Ansichtshalter aussehen sollten:

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

Dieses Beispiel setzt natürlich voraus, dass Sie Ihre Datenquelle (myData) so konstruiert haben, dass Sie auf diese Weise einen Adapter einfach implementieren können. Als Beispiel zeige ich Ihnen, wie ich eine Datenquelle erstellen würde, die eine Liste von Namen und eine Überschrift für jedes Mal zeigt, wenn sich der erste Buchstabe des Namens ändert (vorausgesetzt, die Liste ist alphabetisch geordnet) – ähnlich wie bei Kontakten Liste würde aussehen wie:

 // 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 data = new ArrayList(); 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(); } 

Dieses Beispiel soll ein ziemlich spezifisches Problem lösen, aber ich hoffe, dass Sie damit einen guten Überblick über die Handhabung verschiedener Zeilentypen in einem Recycler erhalten und die erforderlichen Anpassungen in Ihrem eigenen Code vornehmen können, um Ihre Anforderungen zu erfüllen.

Der Trick besteht darin, Unterklassen von ViewHolder zu erstellen und sie anschließend zu konvertieren.

 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; 

Und dann, zur Laufzeit, machen Sie so etwas:

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

Ich hoffe es hilft.

Laut Gil eine großartige Antwort, die ich getriggers habe, indem ich den getItemViewType überschrieben habe, wie von Gil erklärt. Seine Antwort ist großartig und muss als korrekt markiert werden. In jedem Fall füge ich den Code hinzu, um den Punktestand zu erreichen:

In Ihrem Recycler-Adapter:

 @Override public int getItemViewType(int position) { int viewType = 0; // add here your booleans or switch() to set viewType at your needed // IE 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)); } 

Auf diese Weise können Sie jedes benutzerdefinierte Layout für jede Zeile festlegen!

Es ist ziemlich schwierig, aber so viel schwer, kopieren Sie einfach den untenstehenden Code und Sie sind fertig

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

genießen !!!!

Wir können mehrere Ansichten auf einzelne RecyclerView von unten erreichen:

Abhängigkeiten auf Gradle fügen Sie den folgenden Code hinzu: –

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

RecyclerView in XML

  

Aktivitätscode

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

Erstes XML

 < ?xml version="1.0" encoding="utf-8"?>                       

Zweites XML

 < ?xml version="1.0" encoding="utf-8"?>                           

Drittes XML

 < ?xml version="1.0" encoding="utf-8"?>                                   

Jetzt ist Zeit, Adapter zu machen, und das ist wichtig, um die Ansicht “-2” in der gleichen Recycleransicht zu zeigen, also überprüfe diesen Codefokus vollständig: –

 public class CustomAdapter extends RecyclerView.Adapter {    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];    } } 

Sie können auch diesen Link für weitere Informationen überprüfen.

Sie müssen die Methode getItemViewType() in RecyclerView.Adapter implementieren. Standardmäßig gibt onCreateViewHolder(ViewGroup parent, int viewType) Implementierung viewType dieser Methode 0 . Zuerst benötigen Sie den Ansichtstyp des Elements an der Position zum Zweck der Ansichtsrecycling und dafür müssen Sie die Methode getItemViewType() überschreiben, in der Sie viewType die Ihre Position des Elements viewType . Codebeispiel ist unten angegeben

 @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); // ... } 

Sie können ItemViewType einfach zurückgeben und verwenden. Siehe unten stehenden Code:

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

Sie können die Bibliothek verwenden: 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 */ 

Für jedes Element sollten Sie einen ViewRenderer, ViewHolder, SomeModel implementieren:

ViewHolder – es ist ein einfacher Ansichtshalter der Recycleransicht.

SomeModel – es ist Ihr Modell mit ItemModel Schnittstelle

 public class SomeViewRenderer extends ViewRenderer { 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)); } } 

Für weitere Details können Sie sich Dokumentationen ansehen.

Sie können diese Bibliothek verwenden:
https://github.com/kmfish/MultiTypeListViewAdapter (von mir geschrieben)

  • Besser den Code einer Zelle wiederverwenden
  • Bessere Expansion
  • Bessere Entkopplung

Setup-Adapter:

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

Für jede Werbebuchung:

 public class LineListItem1 extends BaseListItem { 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); } }