我正在寻找一个相当于addHeaderView回收视图。基本上我想有2个按钮的图像被添加到列表视图的标题。有不同的方法来添加一个头视图到回收视图吗?举个例子作为指导会很有帮助

编辑2(增加片段布局):

在添加日志语句之后,getViewType似乎只接收到0的位置。这导致onCreateView只加载一个布局:

10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> onCreateViewHolder, viewtype: 0
10-26 16:32:53.766    5449-5449/co.testapp I/logger info﹕ Adapter-> onBindViewHolder, viewType: 0

片段转换来加载CommentFragment:

@Override
public void onPhotoFeedItemClick(View view, int position) {
    if (fragmentManager == null)
        fragmentManager = getSupportFragmentManager();

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    if (view.getId() == R.id.button_comment){
        CommentFragment commentFragment = CommentFragment.newInstance("","", position);
        fragmentTransaction.add(R.id.main_activity, commentFragment,"comment_fragment_tag");
        fragmentTransaction.addToBackStack(Constants.TAG_COMMENTS);
        fragmentTransaction.commit();
    }
}

Fragment的onCreateView:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_comment, container, false);
    mRecyclerView = (RecyclerView) view.findViewById(R.id.list_recylclerview);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(_context));
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    mAdapter = new CommentAdapter(R.layout.row_list_comments, R.layout.row_header_comments, _context, comments);
    mRecyclerView.setAdapter(mAdapter);
    return view;
}

包含recycleview的片段:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context="co.testapp.fragments.CommentFragment"
    android:background="@color/white">
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical">
            <android.support.v7.widget.RecyclerView
                xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/list_recylclerview"
                android:layout_width="match_parent"
                android:layout_height="200dp" />
        </RelativeLayout>
</RelativeLayout>

注释行布局:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent" android:layout_margin="10dp"
    android:background="@color/white">
    <!--Profile Picture-->
    <ImageView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:id="@+id/profile_picture"
        android:background="@color/blue_testapp"/>
    <!--Name-->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="First Name Last Name"
        android:textSize="16dp"
        android:textColor="@color/blue_testapp"
        android:id="@+id/name_of_poster"
        android:layout_toRightOf="@id/profile_picture"
        />
    <!--Comment-->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_marginTop="-5dp"
        android:text="This is a test comment"
        android:textSize="14dp"
        android:textColor="@color/black"
        android:id="@+id/comment"
        android:layout_below="@id/name_of_poster"
        android:layout_toRightOf="@id/profile_picture"/>
</RelativeLayout>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="300dp"
        android:id="@+id/header_photo"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>

适配器代码(感谢hister让我开始):

public class CommentAdapter extends RecyclerView.Adapter<ViewHolder>{

    private final int rowCardLayout;
    public static Context mContext;
    private final int headerLayout;
    private final String [] comments;
    private static final int HEADER = 0;
    private static final int OTHER = 1;

    public CommentAdapter(int rowCardLayout, int headerLayout, Context context, String [] comments) {
        this.rowCardLayout = rowCardLayout;
        this.mContext = context;
        this.comments = comments;
        this.headerLayout = headerLayout;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        logger.i("onCreateViewHolder, viewtype: " + i); //viewtype always returns 0 so OTHER layout is never inflated
        if (i == HEADER) {
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(headerLayout, viewGroup, false);
            return new ViewHolderHeader(v);
        }
        else if (i == OTHER){
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(rowCardLayout, viewGroup, false);
            return new ViewHolderComments(v);
        }
        else 
          throw new RuntimeException("Could not inflate layout");
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        logger.i("onBindViewHolder, viewType: " + i);

        if (viewHolder instanceof ViewHolderComments)
            ((ViewHolderComments) viewHolder).comment.setText(comments[i].toString());
        if (viewHolder instanceof ViewHolderHeader)
           ((ViewHolderHeader) viewHolder).header.setImageResource(R.drawable.image2);
        else {
            logger.e("no instance of viewholder found");
        }
    }

    @Override
    public int getItemCount() {
        int count = comments.length + 1;
        logger.i("getItemCount: " + count);
        return count;
    }

    @Override
    public int getItemViewType(int position) {
        logger.i("getItemViewType position: " + position);
        if (position == HEADER)
            return HEADER;
        else
            return OTHER;
    }

    public static class ViewHolderComments extends RecyclerView.ViewHolder {
        public TextView comment;
        public ImageView image;

        public ViewHolderComments(View itemView) {
            super(itemView);
            comment = (TextView) itemView.findViewById(R.id.comment);
            image = (ImageView) itemView.findViewById(R.id.image);
        }
    }

    public static class ViewHolderHeader extends RecyclerView.ViewHolder {
        public final ImageView header;

        public ViewHolderHeader(View itemView){
            super(itemView);
            header = (ImageView) itemView.findViewById(R.id.header_photo);
        }
    }
}

使用上面的代码,只有头部布局显示为viewType总是0。它是这样的。如果我强制另一个布局,它看起来像这样:


当前回答

HeaderView依赖于LayoutManager。默认的layoutmanager都不支持这一点,而且可能不会。ListView中的HeaderView创建了很多复杂性,但没有任何显著的好处。

我建议创建一个基本适配器类,如果提供的话,它可以为header添加项。不要忘记覆盖notify*方法来适当地偏移它们,这取决于header是否存在。

其他回答

请随意使用我的图书馆,这里有。

它可以让你创建任何RecyclerView的头视图,使用LinearLayoutManager或GridLayoutManager只需一个简单的方法调用。

这就是我的解决方案,它使用了一个抽象类来扩展适配器

abstract class HeaderAdapter<T : RecyclerView.ViewHolder>(headerView: View)
    : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    var headerView: View = headerView
        set(value) {
            notifyItemChanged(HEADER_POSITION)
            field = value
        }
    
    companion object {
        const val TYPE_HEADER = -1
        const val HEADER_POSITION = 0
    }

    abstract val itemCountWithoutHeader: Int
    abstract fun getViewHolder(parent: ViewGroup, viewType: Int): T
    abstract fun onBindItemViewHolder(holder: T, position: Int)

    open fun getItemsItemViewType(position: Int): Int {
        return super.getItemViewType(position)
    }

    final override fun getItemCount(): Int = itemCountWithoutHeader + 1

    final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return if (viewType == TYPE_HEADER) HeaderHolder(headerView) else getViewHolder(parent, viewType)
    }

    final override fun getItemViewType(position: Int): Int {
        return if (position == HEADER_POSITION) TYPE_HEADER else getItemsItemViewType(position)
    }

    final override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (position != HEADER_POSITION) onBindItemViewHolder(holder as T, position-1)
    }

    class HeaderHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}

将向您展示如何在Recycler视图中创建带有项目的header。

步骤1-在gradle文件中添加依赖项。

compile 'com.android.support:recyclerview-v7:23.2.0'
// CardView
compile 'com.android.support:cardview-v7:23.2.0'

卡片视图用于装饰目的。

Step2-制作三个xml文件。 一个是主要活动。其次是标题布局。第三为列表项布局。

activity_main.xml

<android.support.v7.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/my_recycler_view"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

header.xml

<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardElevation="2dp">

    <TextView
        android:id="@+id/txtHeader"
        android:gravity="center"
        android:textColor="#000000"
        android:textSize="@dimen/abc_text_size_large_material"
        android:background="#DCDCDC"
        android:layout_width="match_parent"
        android:layout_height="50dp" />

</android.support.v7.widget.CardView>

list.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardElevation="1dp">

        <TextView
            android:id="@+id/txtName"
            android:text="abc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.v7.widget.CardView>

</LinearLayout>

步骤3-创建三个bean类。

Header.java

public class Header extends ListItem {
    private String header;

    public String getHeader() {
        return header;
    }
    public void setHeader(String header) {
        this.header = header;
    }
}

ContentItem.java

public class ContentItem extends ListItem {

    private String name;
    private String rollnumber;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    public String getRollnumber() {
        return rollnumber;
    }

    public void setRollnumber(String rollnumber) {
        this.rollnumber = rollnumber;
    }
}

ListItem.java

public class ListItem {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

步骤4-创建一个名为MyRecyclerAdapter.java的适配器

public class MyRecyclerAdapter extends  RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEM = 1;

    //Header header;
    List<ListItem> list;
    public MyRecyclerAdapter(List<ListItem> headerItems) {
        this.list = headerItems;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        if (viewType == TYPE_HEADER) {
            View v = inflater.inflate(R.layout.header, parent, false);
            return  new VHHeader(v);
        } else {
            View v = inflater.inflate(R.layout.list, parent, false);
            return new VHItem(v);
        }
        throw new IllegalArgumentException();
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof VHHeader) {
           // VHHeader VHheader = (VHHeader)holder;
            Header  currentItem = (Header) list.get(position);
            VHHeader VHheader = (VHHeader)holder;
            VHheader.txtTitle.setText(currentItem.getHeader());
        } else if (holder instanceof VHItem) 
            ContentItem currentItem = (ContentItem) list.get(position);
            VHItem VHitem = (VHItem)holder;
            VHitem.txtName.setText(currentItem.getName());
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (isPositionHeader(position))
            return TYPE_HEADER;
        return TYPE_ITEM;
    }

    private boolean isPositionHeader(int position) {
        return list.get(position) instanceof Header;
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    class VHHeader extends RecyclerView.ViewHolder{
        TextView txtTitle;
        public VHHeader(View itemView) {
            super(itemView);
            this.txtTitle = (TextView) itemView.findViewById(R.id.txtHeader);
        }
    }
    class VHItem extends RecyclerView.ViewHolder{
        TextView txtName;
        public VHItem(View itemView) {
            super(itemView);
            this.txtName = (TextView) itemView.findViewById(R.id.txtName);
        }
    }
}

步骤5 - 在MainActivity中添加以下代码:

public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    List<List<ListItem>> arraylist;
    MyRecyclerAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        adapter = new MyRecyclerAdapter(getList());
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(adapter);
    }

    private ArrayList<ListItem> getList() {
        ArrayList<ListItem> arrayList = new ArrayList<>();
        for(int j = 0; j <= 4; j++) {
            Header header = new Header();
            header.setHeader("header"+j);
            arrayList.add(header);
            for (int i = 0; i <= 3; i++) {
                ContentItem item = new ContentItem();
                item.setRollnumber(i + "");
                item.setName("A" + i);
                arrayList.add(item);
            }
        }
        return arrayList;
    }

}

函数getList()动态地为标题和列表项生成数据。

如果您想在多个列表之间轻松重用头文件,请查看recyclerview库的1.2.0版本。它引入了ConcatAdapter类,该类将多个适配器连接成一个适配器。因此,你可以创建一个头适配器,并轻松地将它与任何其他适配器结合起来,比如:

myRecyclerView.adapter = ConcatAdapter(headerAdapter, listAdapter)

公告文章包含了如何使用ConcatAdapter在页眉和页脚中显示加载进度的示例。

当我发布这个答案的时候,这个库的1.2.0版本还在alpha阶段,所以api可能会改变。您可以在这里查看状态。

我实现了EC84B4答案中提出的相同方法,但我抽象了RecycleViewAdapter,并通过接口使其易于重用。

所以为了使用我的方法,你应该添加以下基类和接口到你的项目:

1)为适配器提供数据的接口(泛型类型T的集合,以及泛型类型P的附加参数(如果需要))

public interface IRecycleViewListHolder<T,P>{
            P getAdapterParameters();
            T getItem(int position);
            int getSize();
    }

2)用于绑定您的项目(标题/项目)的工厂:

public interface IViewHolderBinderFactory<T,P> {
        void bindView(RecyclerView.ViewHolder holder, int position,IRecycleViewListHolder<T,P> dataHolder);
}

3) viewHolders的工厂(头/项):

public interface IViewHolderFactory {
    RecyclerView.ViewHolder provideInflatedViewHolder(int viewType, LayoutInflater layoutInflater,@NonNull ViewGroup parent);
}

4) Adapter with Header的基类:

public class RecycleViewHeaderBased<T,P> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

    public final static int HEADER_TYPE = 1;
    public final static int ITEM_TYPE = 0;
    private final IRecycleViewListHolder<T, P> dataHolder;
    private final IViewHolderBinderFactory<T,P> binderFactory;
    private final IViewHolderFactory viewHolderFactory;

    public RecycleViewHeaderBased(IRecycleViewListHolder<T,P> dataHolder, IViewHolderBinderFactory<T,P> binderFactory, IViewHolderFactory viewHolderFactory) {
        this.dataHolder = dataHolder;
        this.binderFactory = binderFactory;
        this.viewHolderFactory = viewHolderFactory;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        return viewHolderFactory.provideInflatedViewHolder(viewType,layoutInflater,parent);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            binderFactory.bindView(holder, position,dataHolder);
    }

    @Override
    public int getItemViewType(int position) {
        if(position == 0)
            return HEADER_TYPE;
        return ITEM_TYPE;
    }

    @Override
    public int getItemCount() {
        return dataHolder.getSize()+1;
    }
}

使用的例子:

1) irecycleviewlisholder实现:

public class AssetTaskListData implements IRecycleViewListHolder<Map.Entry<Integer, Integer>, GroupedRecord> {
    private List<Map.Entry<Integer, Integer>> assetCountList;
    private GroupedRecord record;

    public AssetTaskListData(Map<Integer, Integer> assetCountListSrc, GroupedRecord record) {
        this.assetCountList =  new ArrayList<>();
        for(Object  entry: assetCountListSrc.entrySet().toArray()){
            Map.Entry<Integer,Integer> entryTyped = (Map.Entry<Integer,Integer>)entry;
            assetCountList.add(entryTyped);
        }
        this.record = record;
    }

    @Override
    public GroupedRecord getAdapterParameters() {
        return record;
    }

    @Override
    public Map.Entry<Integer, Integer> getItem(int position) {
        return assetCountList.get(position-1);
    }

    @Override
    public int getSize() {
        return assetCountList.size();
    }
}

2) IViewHolderBinderFactory实现:

public class AssetTaskListBinderFactory implements IViewHolderBinderFactory<Map.Entry<Integer, Integer>, GroupedRecord> {
    @Override
    public void bindView(RecyclerView.ViewHolder holder, int position, IRecycleViewListHolder<Map.Entry<Integer, Integer>, GroupedRecord> dataHolder) {
        if (holder instanceof AssetItemViewHolder) {
            Integer assetId = dataHolder.getItem(position).getKey();
            Integer assetCount = dataHolder.getItem(position).getValue();
            ((AssetItemViewHolder) holder).bindItem(dataHolder.getAdapterParameters().getRecordId(), assetId, assetCount);
        } else {
            ((AssetHeaderViewHolder) holder).bindItem(dataHolder.getAdapterParameters());
        }
    }
}

3) IViewHolderFactory实现:

public class AssetTaskListViewHolderFactory implements IViewHolderFactory {
    private IPropertyTypeIconMapper iconMapper;
    private ITypeCaster caster;

    public AssetTaskListViewHolderFactory(IPropertyTypeIconMapper iconMapper, ITypeCaster caster) {
        this.iconMapper = iconMapper;
        this.caster = caster;
    }

    @Override
    public RecyclerView.ViewHolder provideInflatedViewHolder(int viewType, LayoutInflater layoutInflater, @NonNull ViewGroup parent) {
        if (viewType == RecycleViewHeaderBased.HEADER_TYPE) {
            AssetBasedHeaderItemBinding item = DataBindingUtil.inflate(layoutInflater, R.layout.asset_based_header_item, parent, false);
            return new AssetHeaderViewHolder(item.getRoot(), item, caster);
        }
        AssetBasedListItemBinding item = DataBindingUtil.inflate(layoutInflater, R.layout.asset_based_list_item, parent, false);
        return new AssetItemViewHolder(item.getRoot(), item, iconMapper, parent.getContext());
    }
}

4)派生适配器

public class AssetHeaderTaskListAdapter extends RecycleViewHeaderBased<Map.Entry<Integer, Integer>, GroupedRecord> {
   public AssetHeaderTaskListAdapter(IRecycleViewListHolder<Map.Entry<Integer, Integer>, GroupedRecord> dataHolder,
                                      IViewHolderBinderFactory binderFactory,
                                      IViewHolderFactory viewHolderFactory) {
        super(dataHolder, binderFactory, viewHolderFactory);
    }
}

5)实例化适配器类:

private void setUpAdapter() {
        Map<Integer, Integer> objectTypesCountForGroupedTask = groupedTaskRepository.getObjectTypesCountForGroupedTask(this.groupedRecordId);
        AssetTaskListData assetTaskListData = new AssetTaskListData(objectTypesCountForGroupedTask, getGroupedRecord());
        adapter = new AssetHeaderTaskListAdapter(assetTaskListData,new AssetTaskListBinderFactory(),new AssetTaskListViewHolderFactory(iconMapper,caster));
        assetTaskListRecycler.setAdapter(adapter);
    }

注:AssetItemViewHolder, AssetBasedListItemBinding等,我的应用程序自己的结构应该由你自己交换,为你自己的目的。