Recyclerview 和处理不同类型的行膨胀 [英] Recyclerview and handling different type of row inflation

查看:28
本文介绍了Recyclerview 和处理不同类型的行膨胀的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用新的 RecyclerView,但我找不到 RecyclerView 的示例,其中包含不同类型的行/卡片视图变得膨胀.>

使用 ListView 我覆盖了 getViewTypeCountgetItemViewType,以处理不同类型的行.

我应该像旧"方式那样做还是应该用 LayoutManager 做点什么?我想知道是否有人可以指出我正确的方向.因为我只能找到一种类型的例子.

我想要一个略有不同的卡片列表.或者我应该只使用 scrollViewcardViews 里面的...使它没有适配器和 recyclerView?

解决方案

处理类似于 iOS 的 UITableView 的行/节逻辑在 Android 中并不像在 iOS 中那么简单,但是,当您使用 RecyclerView 时 - 什么的灵活性你能做的远不止这些.

最后,关键在于您如何确定您在适配器中显示的视图类型.一旦你弄清楚了,它应该很容易航行(不是真的,但至少你会解决这个问题).

适配器公开了两个您应该覆盖的方法:

getItemViewType(int position)

这个方法的默认实现会一直返回0,表示只有1种视图.在您的情况下,情况并非如此,因此您需要找到一种方法来断言哪一行对应于哪种视图类型.与 iOS 不同,它通过行和节为你管理这个,在这里你将只有一个索引可以依赖,并且你需要使用你的开发技能来知道一个位置何时与节标题相关,以及它何时与普通行.

createViewHolder(ViewGroup parent, int viewType)

无论如何你都需要重写这个方法,但通常人们只是忽略 viewType 参数.根据视图类型,您需要扩充正确的布局资源并相应地创建您的视图持有者.RecyclerView 会以一种避免不同视图类型冲突的方式处理回收不同的视图类型.

如果您打算使用默认的 LayoutManager,例如 LinearLayoutManager,那么您应该很高兴.如果您打算制作自己的 LayoutManager 实现,则需要更努力地工作.您真正需要使用的唯一 API 是 findViewByPosition(int position),它提供特定位置的给定视图.由于您可能希望根据此视图的类型以不同方式对其进行布局,因此您有几个选择:

  1. 通常在使用 ViewHolder 模式时,您使用视图持有者设置视图的标签.您可以在运行时在布局管理器中使用它,通过在视图持有者中添加一个表达这一点的字段来找出视图的类型.

  2. 由于您需要一个函数来确定哪个位置与哪个视图类型相关,您不妨以某种方式使该方法全局可访问(也许是一个管理数据的单例类?),然后您可以简单地根据位置查询同样的方法.

这是一个代码示例:

//在这个示例中,我使用了一个对象数组来模拟列表的数据.//我假设如果对象是一个字符串,这意味着我应该显示一个带有基本标题的标题.//如果不是,我假设它是我创建的一个自定义模型对象,我将用它来绑定我的普通行.私有对象[] myData;public static final int ITEM_TYPE_NORMAL = 0;public static final int ITEM_TYPE_HEADER = 1;公共类 MyAdapter 扩展了 Adapter{@覆盖公共 ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {如果(视图类型 == ITEM_TYPE_NORMAL){查看 normalView = LayoutInflater.from(getContext()).inflate(R.layout.my_normal_row, null);返回新的 MyNormalViewHolder(normalView);//普通项目的视图持有者} else if (viewType == ITEM_TYPE_HEADER) {查看 headerRow = LayoutInflater.from(getContext()).inflate(R.layout.my_header_row, null);返回新的 MyHeaderViewHolder(headerRow);//标题项的视图持有者}}@覆盖公共无效 onBindViewHolder(ViewHolder 持有人,int 位置){final int itemType = getItemViewType(position);如果(itemType == ITEM_TYPE_NORMAL){((MyNormalViewHolder)holder).bindData((MyModel)myData[position]);} else if (itemType == ITEM_TYPE_HEADER) {((MyHeaderViewHolder)holder).setHeaderText((String)myData[position]);}}@覆盖公共 int getItemViewType(int position) {if (myData[position] instanceof String) {返回 ITEM_TYPE_HEADER;} 别的 {返回 ITEM_TYPE_NORMAL;}}@覆盖公共 int getItemCount() {返回 myData.length;}}

以下是这些视图持有者的示例:

public MyHeaderViewHolder 扩展 ViewHolder {私有 TextView headerLabel;公共 MyHeaderViewHolder(查看视图){超级(查看);headerLabel = (TextView)view.findViewById(R.id.headerLabel);}公共无效setHeaderText(字符串文本){headerLabel.setText(text);}}公共 MyNormalViewHolder 扩展 ViewHolder {私有 TextView 标题标签;私人文本视图描述标签;公共 MyNormalViewHolder(查看视图){超级(查看);titleLabel = (TextView)view.findViewById(R.id.titleLabel);descriptionLabel = (TextView)view.findViewById(R.id.descriptionLabel);}公共无效绑定数据(我的模型模型){titleLabel.setText(model.getTitle());descriptionLabel.setText(model.getDescription());}}

当然,此示例假定您已经以一种易于以这种方式实现适配器的方式构建了数据源 (myData).作为示例,我将向您展示如何构建一个数据源,该数据源显示姓名列表,以及每次姓名的第一个字母更改时的标题(假设列表按字母顺序排列) - 类似于联系人的方式列表看起来像:

//假设名称 &描述是非空的并且具有相同的长度.//假设名称按字母顺序排列私有无效过程数据源(字符串 [] 名称,字符串 [] 描述){String nextFirstLetter = "";字符串 currentFirstLetter;列表<对象>数据 = 新的 ArrayList();for (int i = 0; i 

这个例子解决了一个相当具体的问题,但我希望这能让你很好地了解如何在回收器中处理不同的行类型,并允许你在自己​​的代码中进行必要的调整以满足你的需求.

I'm trying to work with the new RecyclerView, but I could not find an example of a RecyclerView with different types of rows/cardviews getting inflated.

With ListView I override the getViewTypeCount and getItemViewType, for handling different types of rows.

Am I supposed to do it like the "old" way or should I do something with LayoutManager? I was wondering if someone could point me to the right direction. Because I can only find examples with one type.

I want to have a list of slightly different cards. Or should I just use a scrollView with cardViews inside of it...make it without the adapter and recyclerView?

解决方案

Handling the rows / sections logic similar to iOS's UITableView is not as simple in Android as it is in iOS, however, when you use RecyclerView - the flexibility of what you can do is far greater.

In the end, it's all about how you figure out what type of view you're displaying in the Adapter. Once you got that figured out, it should be easy sailing (not really, but at least you'll have that sorted).

The Adapter exposes two methods which you should override:

getItemViewType(int position)

This method's default implementation will always return 0, indicating that there is only 1 type of view. In your case, it is not so, and so you will need find a way to assert which row corresponds to which view type. Unlike iOS, which manages this for you with rows and sections, here you will have only one index to rely on, and you'll need to use your developer skills to know when a position correlates to a section header, and when it correlates to a normal row.

createViewHolder(ViewGroup parent, int viewType)

You need to override this method anyway, but usually people just ignore the viewType parameter. According to the view type, you'll need to inflate the correct layout resource and create your view holder accordingly. The RecyclerView will handle recycling different view types in a way which avoids clashing of different view types.

If you're planning on using a default LayoutManager, such as LinearLayoutManager, you should be good to go. If you're planning on making your own LayoutManager implementation, you'll need to work a bit harder. The only API you really have to work with is findViewByPosition(int position) which gives a given view at a certain position. Since you'll probably want to lay it out differently depending on what type this view is, you have a few options:

  1. Usually when using the ViewHolder pattern, you set the view's tag with the view holder. You could use this during runtime in the layout manager to find out what type the view is by adding a field in the view holder which expresses this.

  2. Since you'll need a function which determines which position correlates to which view type, you might as well make this method globally accessible somehow (maybe a singleton class which manages the data?), and then you can simply query the same method according to the position.

Here's a code sample:

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

Here's a sample of how these view holders should look like:

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

Of course, this sample assumes you've constructed your data source (myData) in a way that makes it easy to implement an adapter in this way. As an example, I'll show you how I'd construct a data source which shows a list of names, and a header for every time the 1st letter of the name changes (assume the list is alphabetized) - similar to how a contacts list would look like:

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

This example comes to solve a fairly specific issue, but I hope this gives you a good overview on how to handle different row types in a recycler, and allows you make the necessary adaptations in your own code to fit your needs.

这篇关于Recyclerview 和处理不同类型的行膨胀的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆