https://developer.android.com/preview/material/ui-widgets.html adresinden
RecyclerView.Adapteroluşturduğumuzda, adaptör ile bağlanacak
ViewHolder` belirtmeliyiz.
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;
}
}
Peki, birden fazla görünüm türüne sahip RecyclerView
oluşturmak mümkün mü?
Evet, bu mümkün. Sadece getItemViewType() uygulayın ve onCreateViewHolder()
içindeki viewType
parametresine dikkat edin.
Yani şöyle bir şey yaparsınız:
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;
}
}
}
Görünüm türleri için düzenler yalnızca birkaç taneyse ve bağlama mantığı basitse, Anton'un çözümünü izleyin.
Ancak karmaşık düzenleri ve bağlama mantıklarını yönetmeniz gerekiyorsa kod dağınık olacaktır.
Aşağıdaki çözümün karmaşık görünüm türlerini ele alması gereken biri için yararlı olacağına inanıyorum.
Temel DataBinder sınıfı
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();
......
}
Bu sınıfta tanımlanması gereken fonksiyonlar, tek görünüm tipi oluştururken adaptör sınıfıyla hemen hemen aynıdır.
Her görünüm türü için, bu DataBinder'ı genişleterek sınıfı oluşturun.
Örnek DataBinder sınıfı
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);
}
}
}
DataBinder sınıflarını yönetmek için adaptör sınıfı oluşturun.
Temel DataBindAdapter sınıfı
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);
......
}
Bu temel sınıfı genişleterek sınıfı oluşturun ve ardından DataBinder sınıflarını örnekleyin ve soyut yöntemleri geçersiz kılın
getItemCount DataBinders'ın toplam öğe sayısını döndürür
getItemViewType Adaptör konumu ve görünüm türü arasındaki eşleme mantığını tanımlayın.
getDataBinder Görünüm türüne göre DataBinder örneğini döndürür
getPosition Belirtilen DataBinder'daki konumdan bağdaştırıcı konumuna dönüştürme mantığını tanımlama
getBinderPosition Adaptör konumundan DataBinder'daki konuma dönüştürme mantığını tanımlayın
Umarım bu çözüm yardımcı olur.
GitHub'da daha ayrıntılı çözüm ve örnekler bıraktım, bu nedenle ihtiyacınız olursa lütfen aşağıdaki bağlantıya bakın.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter
Anton'un çözümünü izleyerek, farklı düzen türlerini tutan / işleyen / temsil eden bu ViewHolder
ile gelin.
Ancak, geri dönüşüm görünümünün ViewHolder
ı veri yuvarlama türü olmadığında yeni düzenin değiştirilmesinin işe yarayıp yaramayacağından emin değilim.
Yani kısaca, onCreateViewHolder(ViewGroup parent, int viewType)` sadece yeni görünüm düzeni gerektiğinde çağrılır;
viewType
için getItemViewType(int position)
çağrılacaktır;
onBindViewHolder(ViewHolder holder, int position)her zaman görünümü geri dönüştürürken çağrılır (yeni veri getirilir ve bu
ViewHolder` ile görüntülenmeye çalışılır).
Dolayısıyla onBindViewHolder
çağrıldığında doğru görünüm düzenini yerleştirmesi ve ViewHolder
ı güncellemesi gerekir.
Bu ViewHolder
ın getirilmesi için görünüm düzenini değiştirmenin yolu doğru mu veya herhangi bir sorun var mı?
Her türlü yorum için minnettarım!
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);
}
Düzenle: ViewHolder, görünümü tutmak için mItemViewType üyesine sahiptir
Düzenleme: onBindViewHolder(ViewHolder holder, int position)'da aktarılan ViewHolder, eşleştiğinden emin olmak için getItemViewType(int position)'a bakılarak alınmış (veya oluşturulmuş) gibi görünüyor, bu nedenle ViewHolder'ın türünün data[position]'s türüyle eşleşmemesi konusunda endişelenmenize gerek olmayabilir. onBindViewHolder() içindeki ViewHolder'ın nasıl alındığını bilen var mı?
Düzenleme: Geri dönüşüm ViewHolder
türe göre seçilmiş gibi görünüyor, bu yüzden orada savaşçı yok.
Düzenleme: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ bu soruyu yanıtlıyor.
Geri dönüşüm ViewHolder
gibi olur:
holder = getRecycledViewPool().getRecycledView(mAdapter.getItemViewType(offsetPosition));
veya doğru türde geri dönüşüm ViewHolder
bulamazsanız yeni bir tane oluşturun.
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;
}