使用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吗?


当前回答

比以往任何时候都简单,忘记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

其他回答

如果有人有兴趣看看用Kotlin编写的超级简单的解决方案,请查看我刚刚创建的博客文章。博客中的例子是基于创建Sectioned RecyclerView的:

https://brona.blog/2020/06/sectioned-recyclerview-in-three-steps/

下面的代码不是伪代码。我已经测试过了,它对我很有效。

我想在我的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));
            }
        }
    }

首先,必须创建两个布局XML文件。之后,在recyclerview适配器中,TYPE_CALL和TYPE_EMAIL是两个静态值,分别为适配器类中的1和2。

现在在Recycler视图Adapter类级别定义两个静态值,例如:private static int TYPE_EMAIL = 2;

现在创建多个视图的视图持有者,如下所示:

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

现在在recyclerview适配器的onCreateViewHolder和onBindViewHolder方法中编写如下代码:

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

实际上,我想改进一下安东的回答。

由于getItemViewType(int position)返回一个整数值,您可以返回需要膨胀的布局资源ID。这样你就可以在onCreateViewHolder(ViewGroup parent, int viewType)方法中保存一些逻辑。

此外,我不建议在getItemCount()中进行密集的计算,因为在呈现列表时,以及在呈现可见项之外的每个项时,该特定函数至少被调用了5次。遗憾的是,由于notifyDatasetChanged()方法是final方法,所以不能重写它,但是可以从适配器中的另一个函数调用它。

比以往任何时候都简单,忘记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