【问题标题】:Create a RecyclerView with multiple view from layouts从布局创建具有多个视图的 RecyclerView
【发布时间】:2018-12-08 12:42:15
【问题描述】:

我试图在一个 RecyclerView 中有 2 个布局

我有一个名为 Bookmark 的回收器视图列表,它是从 xml 解析的,这一切正常,但我想在这个回收器视图中放置另一个布局,其中包含一个按钮并且可以点击。 就像照片中的图标来自recyclerview,加号按钮需要与列表兼容,如果列表更大或更小,按钮将与列表空间兼容。

这是我的适配器的新代码,取决于@LluisFelisart 的答案 这就是错误 ViewHolder views must not be attached when created. Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)

    public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Context context;
    ArrayList<Bookmark> arrayList = new ArrayList<>();

    public MyAdapter(Context context, ArrayList<Bookmark> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    public class ViewHolder0 extends RecyclerView.ViewHolder {

        TextView tvName,tvId,tvSearchUrl,tvNativeUrl;
        ImageView tvIcon;

        public ViewHolder0(@NonNull  View itemView) {
            super(itemView);

            tvName=itemView.findViewById(R.id.textView);
            tvIcon = itemView.findViewById(R.id.image_view);
         /*   tvId=itemView.findViewById(R.id.tvId);
            tvSearchUrl=itemView.findViewById(R.id.tvSearchUrl);
            tvNativeUrl=itemView.findViewById(R.id.tvNativeUrl);*/
        }
    }

    public class ViewHolder2 extends RecyclerView.ViewHolder {
        ImageView tvAddBookmark;

        public ViewHolder2(@NonNull View itemView) {
            super(itemView);
            tvAddBookmark = itemView.findViewById(R.id.image_button_add);
        }
    }
    @Override
    public int getItemViewType(int position) {
        // Just as an example, return 0 or 2 depending on position
        // Note that unlike in ListView adapters, types don't have to be contiguous
        return position % 2 * 2;
    }




    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.grid_item, viewGroup, false);

        switch (i) {
            case 0: return new ViewHolder0(viewGroup);
            case 2: return new ViewHolder2(viewGroup);
        }

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        switch (holder.getItemViewType()) {
            case 0:
                ViewHolder0 viewHolder0 = (ViewHolder0) holder;
                ((ViewHolder0) holder).tvName.setText(arrayList.get(position).getName());
                ((ViewHolder0) holder).tvIcon.setImageResource(arrayList.get(position).getIcon());
                break;

            case 2:
                ViewHolder2 viewHolder2 = (ViewHolder2) holder;

        }
        ((ViewHolder0) holder).tvIcon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent;
                intent = new Intent(context, BookmarkActivity.class);
                v.getContext().startActivity(intent);
            }
        });

        ((ViewHolder0) holder).tvIcon.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                Intent intent = new Intent(context, ActivityBookmarksFavorites.class);
                v.getContext().startActivity(intent);
                return false;
            }
        });
    }




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

    public class ViewHolder extends RecyclerView.ViewHolder  {

        TextView tvName,tvId,tvSearchUrl,tvNativeUrl;
        ImageView tvIcon;

        public ViewHolder(@NonNull  View itemView) {
            super(itemView);

            tvName=itemView.findViewById(R.id.textView);
            tvIcon = itemView.findViewById(R.id.image_view);
         /*   tvId=itemView.findViewById(R.id.tvId);
            tvSearchUrl=itemView.findViewById(R.id.tvSearchUrl);
            tvNativeUrl=itemView.findViewById(R.id.tvNativeUrl);*/
        }



    }



}

这是网格项目布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:orientation="vertical"
    android:visibility="visible"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <ImageView
        android:id="@+id/image_view"
        style="@style/BookmarkIconIv" />
    <TextView android:id="@+id/textView"
        android:layout_marginTop="1.0dip"
        style="@style/BookmarkTextTv" />
</LinearLayout>

这是我想要在回收站视图中的按钮布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageButton
        android:id="@+id/image_button_add"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:src="@drawable/ic_add_black_24dp"
        android:background="@color/transparent" />

</android.support.constraint.ConstraintLayout>

这是回收站显示的片段

public class FragmentBookmark extends Fragment {
    ArrayList<Bookmark> arrayList = new ArrayList<>();
    XmlPullParser pullParser;
    MyAdapter myAdapter;
    View paramView;
    RecyclerView myRecyclerView;
    private Context mContext;
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mContext = context;
    }
    @Nullable
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
       paramView = inflater.inflate(R.layout.bookmark, container, false);

        myRecyclerView =  paramView.findViewById(R.id.myRecyclerView);
        myRecyclerView.setLayoutManager(new GridLayoutManager(mContext, 4));
        myRecyclerView.setHasFixedSize(true);
        myAdapter = new MyAdapter(mContext, arrayList);
        myRecyclerView.setAdapter(myAdapter);
        try {
            XmlPullParser xpp = getResources().getXml(R.xml.bookmarks);
            while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
                if (xpp.getEventType() == XmlPullParser.START_TAG) {
                    if (xpp.getName().equals("Bookmark")) {
                        Bookmark bookmark = new Bookmark();
                        bookmark.setName(xpp.getAttributeValue(null, "name"));
                        int drawableResourceId = getResources().getIdentifier(xpp.getAttributeValue(null, "icon"),"drawable", mContext.getPackageName());
                        bookmark.setIcon(drawableResourceId);
                        arrayList.add(bookmark);
                    }
                }
                xpp.next();
            }
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        myAdapter.notifyDataSetChanged();
        return paramView;
    }
    }

这是包含recyclerview的布局书签

<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/myRecyclerView"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_marginBottom="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    android:fillViewport="false">

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

</LinearLayout>

【问题讨论】:

  • 您可以使用自定义itemViewType 并将其作为 RecyclerView 中的项目进行膨胀。
  • 你能提供一个代码如何做到这一点。或者至少给我一个关于我的代码的帮助。
  • @Spritzig 你能把你的代码作为示例项目添加到 Github 吗??
  • 我将尝试在 Github 中制作示例项目
  • @Spritzig 好的,我会检查一下,你能解释一下你在上面的代码中遇到了什么问题,你的预期输出是什么

标签: android android-layout android-recyclerview


【解决方案1】:

这就是答案

按照这些步骤进行

首先为您的 Multiple viewType 创建两个布局

示例代码

layout.layout_one

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:orientation="vertical"
    app:cardCornerRadius="15dp"
    app:cardElevation="5dp"
    app:cardUseCompatPadding="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="Name  : " />

            <TextView
                android:id="@+id/tvName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="Icon  : " />

            <ImageView
                android:id="@+id/tvIcon"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:padding="10dp"
                android:text="" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="Id  : " />

            <TextView
                android:id="@+id/tvId"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="SearchUrl  : " />

            <TextView
                android:id="@+id/tvSearchUrl"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="NativeUrl  : " />

            <TextView
                android:id="@+id/tvNativeUrl"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="" />

        </LinearLayout>

    </LinearLayout>

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

layout.button_two

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imgButton"
        android:layout_width="50dp"
        android:layout_height="50dp" />
</LinearLayout>

现在你需要为你的两个 viewType 创建两个 RecyclerView.ViewHolder

现在你需要覆盖getItemViewType()

  • 它返回位置项的viewType,用于视图回收。

现在在您的 onCreateViewHolder() 方法中,您需要根据您将使用 getItemViewType() 方法获得的 viewType 返回您的 ViewHolder 实例

比在您的 onBindViewHolder() 方法中基于您的 viewType 设置您的视图属性

这里是RecyclerView.Adapter的示例代码,有多种视图类型

数据适配器

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;

public class DataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private Context context;
    ArrayList<Bookmark> arrayList = new ArrayList<>();
    public static final int ITEM_TYPE_ONE = 0;
    public static final int ITEM_TYPE_TWO = 1;

    public DataAdapter(Context context, ArrayList<Bookmark> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        View view = null;
        // check here the viewType and return RecyclerView.ViewHolder based on view type
        if (viewType == ITEM_TYPE_ONE) {
            view = LayoutInflater.from(context).inflate(R.layout.layout_one, parent, false);
            return new ViewHolder(view);
        } else if (viewType == ITEM_TYPE_TWO) {
            view = LayoutInflater.from(context).inflate(R.layout.button_two, parent, false);
            return new ButtonViewHolder(view);
        }else {
            return  null;
        }

    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        final int itemType = getItemViewType(position);
        // First check here the View Type
        // than set data based on View Type to your recyclerview item
        if (itemType == ITEM_TYPE_ONE) {
            ViewHolder viewHolder = (ViewHolder) holder;
            viewHolder.tvName.setText(arrayList.get(position).getName());
            viewHolder.tvIcon.setImageResource(arrayList.get(position).getIcon());
            viewHolder.tvSearchUrl.setText(arrayList.get(position).getSearchUrl());
            viewHolder.tvNativeUrl.setText(arrayList.get(position).getNativeUrl());
        } else if (itemType == ITEM_TYPE_TWO) {
            ButtonViewHolder buttonViewHolder = (ButtonViewHolder) holder;
            buttonViewHolder.imgButton.setImageResource(arrayList.get(position).getIcon());
        }

    }

    @Override
    public int getItemViewType(int position) {
        // based on you list you will return the ViewType 
        if (arrayList.get(position).getViewType() == 0) {
            return ITEM_TYPE_ONE;
        } else {
            return ITEM_TYPE_TWO;
        }
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder {

        TextView tvName, tvId, tvSearchUrl, tvNativeUrl;

        ImageView tvIcon;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            tvName = itemView.findViewById(R.id.tvName);
            tvIcon = itemView.findViewById(R.id.tvIcon);
            tvId = itemView.findViewById(R.id.tvId);
            tvSearchUrl = itemView.findViewById(R.id.tvSearchUrl);
            tvNativeUrl = itemView.findViewById(R.id.tvNativeUrl);
        }
    }

    public class ButtonViewHolder extends RecyclerView.ViewHolder {


        ImageView imgButton;

        public ButtonViewHolder(@NonNull View itemView) {
            super(itemView);

            imgButton = itemView.findViewById(R.id.imgButton);

        }
    }
}

在列表中添加数据时,您需要在列表中提供viewtype

在您的 Bookmark POJO 类中进行一些更改

BookmarkPOJO类

public class Bookmark
{
    String name,id,nativeUrl,searchUrl;
    int icon;

    int viewType;

    public String getName() {
        return name;
    }

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

    public String getId() {
        return id;
    }

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

    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public String getNativeUrl() {
        return nativeUrl;
    }

    public void setNativeUrl(String nativeUrl) {
        this.nativeUrl = nativeUrl;
    }

    public String getSearchUrl() {
        return searchUrl;
    }

    public void setSearchUrl(String searchUrl) {
        this.searchUrl = searchUrl;
    }

    public int getViewType() {
        return viewType;
    }

    public void setViewType(int viewType) {
        this.viewType = viewType;
    }

    @Override
    public String toString() {
        return "Bookmark{" +
                "name='" + name + '\'' +
                ", icon='" + icon + '\'' +
                ", id='" + id + '\'' +
                ", nativeUrl='" + nativeUrl + '\'' +
                ", searchUrl='" + searchUrl + '\'' +
                '}';
    }
}

示例活动代码

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private Context mContext;
    ArrayList<Bookmark> arrayList = new ArrayList<>();

    RecyclerView myRecyclerView;
    DataAdapter dataAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        mContext = this;

        myRecyclerView = findViewById(R.id.myRecyclerView);
        myRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
        myRecyclerView.setHasFixedSize(true);

        dataAdapter = new DataAdapter(mContext, arrayList);
        myRecyclerView.setAdapter(dataAdapter);

        try {

            XmlPullParser xpp = getResources().getXml(R.xml.bookmarks);

            while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
                if (xpp.getEventType() == XmlPullParser.START_TAG) {
                    if (xpp.getName().equals("Bookmark")) {

                        Log.e("MY_VALUE", " * " + xpp.getAttributeValue(0) + " * ");
                        Log.e("MY_VALUE", " * " + xpp.getAttributeValue(1) + " * ");
                        Log.e("MY_VALUE", " * " + xpp.getAttributeValue(5) + " * ");
                        Log.e("MY_VALUE", " * " + xpp.getAttributeValue(2) + " * ");
                        Log.e("MY_VALUE", " * " + xpp.getAttributeValue(3) + " * ");
                        Log.e("MY_VALUE", " * " + xpp.getAttributeValue(4) + " * ");


                        Bookmark bookmark = new Bookmark();
                        bookmark.setName(xpp.getAttributeValue(0));

                        int drawableResourceId = this.getResources().getIdentifier(xpp.getAttributeValue(1), "drawable", mContext.getPackageName());
                        bookmark.setIcon(drawableResourceId);

                        bookmark.setId(xpp.getAttributeValue(2));

                        bookmark.setSearchUrl(xpp.getAttributeValue(3));
                        bookmark.setNativeUrl(xpp.getAttributeValue(4));

                        // here you need to set view type
                        bookmark.setViewType(0);
                        arrayList.add(bookmark);
                    }
                }

                xpp.next();
            }
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // here i have added second viewType
        // you need to set as per your requirement
        Bookmark bookmark = new Bookmark();
        bookmark.setViewType(1);
        bookmark.setIcon(R.drawable.dishu);
        arrayList.add(bookmark);
        dataAdapter.notifyDataSetChanged();

    }


}

注意

在下面的代码中,我在Arraylist 的最后一个索引处设置了第二个viewType 您需要根据您的要求设置viewType

有关更多信息,您可以查看以下文章

【讨论】:

  • 按钮视图持有者ButtonViewHolder buttonViewHolder = (ButtonViewHolder) holder;这里让我感到困惑的是错误Cannot cast ViewHolder to ButtonViewHolder
  • @Spritzig 请检查整个DataAdapter 类我在那个适配器类中做了很多改变
  • 你能帮我解决我遇到的另一个问题吗,你知道你已经回答了我从 xml 解析数据,你知道现在如何在那里添加新数据,我有一个布局我可以使用共享首选项放置字符串并保存,但我不知道如何在 recyclerview 列表中显示。
  • @Spritzig 您不能在运行时在res 文件夹中添加新数据
【解决方案2】:

您可以在同一个 RecyclerView 上使用不同的布局,只需覆盖适配器 getItemViewType() 方法并为按钮布局返回不同的 int 值,在您的示例中,您应该为普通项目返回例如 1,为正常项目返回 2按钮项。

视图类型作为参数传递给onCreateViewHolder() 方法,并根据viewType 值扩展普通布局或按钮布局。

您似乎还需要使getItemCount() 返回比数组大小多一

希望对你有帮助

这里是一个例子: How to create RecyclerView with multiple view type?

【讨论】:

  • 谢谢你的回答,但我有点困惑你能提供一个关于你的答案的代码吗?
  • 我已经添加了代码但它不起作用,我正在更改代码以查看我做错了什么。
【解决方案3】:

在您的 Activity 中执行此操作。

        ArrayList<Bookmark>  data = new ArrayList<>();
        //data.addAll(your array list bookmark); uncomment this line add your all array list of bookmark
        Bookmark d = new Bookmark(0);
        data.add(d);
        mList.setAdapter(new BookMarkAdapter(activity, data));

试试这个适配器

public class BookMarkAdapter extends RecyclerView.Adapter {

private Context context;
private ArrayList<Bookmark> data;

public BookMarkAdapter(Context context, ArrayList<Bookmark> data) {
    this.context = context;
    this.data = data;
}

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    if (viewType == 1)
        return new ViewBookmarkHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_with_normal_image_and_textview, parent, false));
    else
        return new AddBookmarkHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_with_image, parent, false));

}


@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

    Bookmark d = data.get(position);

     if (d.getType()==1) {
        ViewBookmarkHolder viewBookmarkHolder =(ViewBookmarkHolder) holder;
        // do your show image and textview operation here
    } else {

        AddBookmarkHolder addBookmarkHolder =(AddBookmarkHolder) holder;
        // do your on click operation here. Like adding new bookmark and update your arraylist and notify data changed for adapter.
    }
}

@Override
public int getItemViewType(int position) {
    return data.get(position).getType();
}

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

在您的 Bookmark Pojo 中更新此方法和变量

public class Bookmark {

    private Integer type;

    public Bookmark(Integer type) {
        this.type = type;
    }

    public void setType(Integer type) {
        this.type = type;
    }

    public Integer getType() {
        if(type==null)
            return 1;
        return type;
    }
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-26
    • 2014-12-02
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多