Recyclerview和处理不同类型的行通胀

Lok*_*kio 123 android listview android-recyclerview

我正在尝试使用新的RecyclerView,但我找不到一个RecyclerView不同类型的行/卡片视图被夸大的例子.

随着ListView我覆盖getViewTypeCountgetItemViewType,用于处理不同类型的行.

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

我想要一张略有不同卡片的清单.或者我应该只使用一个scrollViewcardViews它里面......度过,即使没有适配器和recyclerView

Gil*_*yof 197

处理类似于iOS的UITableView的行/部分逻辑在Android中并不像在iOS中那么简单,但是,当您使用RecyclerView时 - 您可以做的灵活性要大得多.

最后,关于如何确定适配器中显示的视图类型.一旦你明白这一点,它应该很容易航行(不是真的,但至少你会有那个排序).

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

getItemViewType(int position)
Run Code Online (Sandbox Code Playgroud)

此方法的默认实现将始终返回0,表示只有一种类型的视图.在您的情况下,情况并非如此,因此您需要找到一种方法来断言哪一行对应于哪种视图类型.与通过行和部分为您管理此操作的iOS不同,此处您只需依赖一个索引,并且您需要使用开发人员技能来了解某个位置何时与节标题相关联,以及何时与其相关联一个正常的行.

createViewHolder(ViewGroup parent, int viewType)
Run Code Online (Sandbox Code Playgroud)

您无论如何都需要覆盖此方法,但通常人们只是忽略viewType参数.根据视图类型,您需要膨胀正确的布局资源并相应地创建视图持有者.RecyclerView将以避免不同视图类型冲突的方式处理回收不同的视图类型.

如果你打算使用默认的LayoutManager,比如LinearLayoutManager,你应该好好去.如果您计划制作自己的LayoutManager实现,则需要更加努力.您真正需要使用的唯一API是findViewByPosition(int position)在特定位置提供给定视图.由于您可能希望根据此视图的类型进行不同的布局,因此您有以下几种选择:

  1. 通常在使用ViewHolder模式时,您可以使用视图持有者设置视图的标记.您可以在布局管理器的运行时期间使用它来通过在视图持有者中添加表示此视图的字段来查找视图的类型.

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

这是一个代码示例:

// 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是这些视图持有者应如何显示的示例:

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());
    }    
}
Run Code Online (Sandbox Code Playgroud)

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

// 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();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 精美解释!通常我们会查找代码片段,但这确实提供了更好的理解.奖励! (13认同)

tic*_*fab 112

诀窍是创建ViewHolder的子类,然后转换它们.

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;  
Run Code Online (Sandbox Code Playgroud)

然后,在运行时执行以下操作:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.

  • 开门见山.这应该是公认的答案. (4认同)
  • 这是对问题的直接回答.唯一缺少的部分是需要覆盖onCreateViewHolder(ViewGroup parent,int viewType)并根据viewType处理不同的视图类型 (3认同)

iGi*_*o90 31

根据Gil的好回答,我通过重写getItemViewType来解决,如Gil所解释的那样.他的答案很棒,必须标记为正确.无论如何,我添加代码来达到分数:

在您的回收器适配器中:

@Override
public int getItemViewType(int position) {
    int viewType = 0;
    // add here your booleans or switch() to set viewType at your needed
    // I.E 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));
}
Run Code Online (Sandbox Code Playgroud)

通过这样做,您可以为任何行设置任何自定义布局!

  • 只是一个小评论:onCreateViewHolder中的第二个参数应该是viewType,而不是索引.根据API:https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#onCreateViewHolder(android.view.ViewGroup,int) (18认同)

yub*_*del 14

这很棘手但是很难,只需复制下面的代码即可完成

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<NavDrawerAdapter.MainViewHolder> {
    List<MainOption> 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);
        }
    }


}
Run Code Online (Sandbox Code Playgroud)

请享用 !!!!