使用RecyclerView创建动态列表:
当我们创建一个RecyclerView时。适配器我们必须指定ViewHolder,它将绑定到适配器。
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;
}
}
有可能创建多个视图类型的RecyclerView吗?
是的,这是可能的。
写一个通用的视图持有者:
public abstract class GenericViewHolder extends RecyclerView.ViewHolder
{
public GenericViewHolder(View itemView) {
super(itemView);
}
public abstract void setDataOnView(int position);
}
然后创建你的视图持有者,并让他们扩展GenericViewHolder。比如这个:
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);
}
}
}
然后是RecyclerView。适配器类看起来像这样:
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);
}
比以往任何时候都简单,忘记ViewTypes。不建议在一个适配器中使用多个视图类型。这将使代码混乱,并打破单一责任原则,因为现在适配器需要处理逻辑来知道要填充哪个视图。
现在,想象一下在大型团队中工作,每个团队都必须在这些视图类型中的一种功能中工作。如果在不同视图类型中工作的所有团队都使用同一个适配器,那将是一团糟。这是使用ConcatAdapter解决的,其中隔离了适配器。逐个编码,然后将它们合并到一个视图中。
在recyclerview:1.2.0-alpha04中,您现在可以使用ConcatAdapter。
如果你需要一个具有不同viewTypes的视图,你可以为每个部分编写适配器,并使用ConcatAdapter将它们合并到一个recyclerview中。
ConcatAdapter
这张图片显示了一个recyclerview拥有的三种不同的视图类型,页眉,内容和页脚。
你只需要为每个section创建一个适配器,然后使用ConcatAdapter将它们合并到一个recyclerview中:
val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …
val concatAdapter = ConcatAdapter(firstAdapter, secondAdapter,
thirdAdapter)
recyclerView.adapter = concatAdapter
这就是你需要知道的。如果您想处理加载状态,例如在某些加载发生后删除最后一个适配器,您可以使用LoadState。
欲了解更多深入信息,请关注Florina Muntenescu的帖子https://medium.com/androiddevelopers/merge-adapters-sequentially-with-mergeadapter-294d2942127a
下面的代码不是伪代码。我已经测试过了,它对我很有效。
我想在我的recyclerview中创建一个headerview,然后在用户可以点击的标题下面显示一个图片列表。
我在我的代码中使用了一些开关,不知道这是否是最有效的方法,所以请随意给出您的评论:
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;
}
}
//Overridden 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 it's 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));
}
}
}
是的,这是可能的。
写一个通用的视图持有者:
public abstract class GenericViewHolder extends RecyclerView.ViewHolder
{
public GenericViewHolder(View itemView) {
super(itemView);
}
public abstract void setDataOnView(int position);
}
然后创建你的视图持有者,并让他们扩展GenericViewHolder。比如这个:
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);
}
}
}
然后是RecyclerView。适配器类看起来像这样:
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);
}
如果你想将它与Android数据绑定结合使用,请查看https://github.com/evant/binding-collection-adapter -它是迄今为止我所见过的多种视图类型RecyclerView的最佳解决方案。
你可以这样使用它
var items: AsyncDiffPagedObservableList<BaseListItem> =
AsyncDiffPagedObservableList(GenericDiff)
val onItemBind: OnItemBind<BaseListItem> =
OnItemBind { itemBinding, _, item -> itemBinding.set(BR.item, item.layoutRes) }
然后在列表所在的布局中
<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}"/>
你的列表项必须实现BaseListItem接口,它看起来像这样:
interface BaseListItem {
val layoutRes: Int
}
项目视图应该看起来像这样:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="...presentation.somescreen.list.YourListItem"/>
</data>
...
</layout>
YourListItem实现BaseListItem。
虽然选择的答案是正确的,但我只是想进一步阐述它。我发现一个有用的自定义适配器为多种视图类型在RecyclerView。
它的Kotlin版本在这里。
自定义适配器如下:
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;
}
}