将布局动态添加到另一个布局
Dynamically add layout to another layout
我有一个 RecyclerView 适配器。在 onBindViewHolder
中,我检查传递给适配器的每个项目有多少图像。根据图像的数量,我想 load/inflate 一个子布局到主布局中。
例如:
- 如果项目有 1 张图片,我需要将
images_one.xml
膨胀到主布局中
- 如果项目有 2 张图片,我需要将
images_two.xml
膨胀到主布局中
- 等等,最多 8 张图片 (
images_eight.xml
)
这里是主要布局,main_layout.xml
:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
// INFLATE CHILD LAYOUT HERE (images_one.xml, images_two.xml, etc.)
<TextView
android:id="@+id/desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
这是需要膨胀到主布局中的子布局之一,images_two.xml
:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/image_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/image_two"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
最后,这是我的 RecyclerView 适配器:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private static Context context;
private List<Message> mDataset;
public RecyclerAdapter(Context context, List<Message> myDataset) {
this.context = context;
this.mDataset = myDataset;
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener, View.OnClickListener {
public TextView title;
public TextView desc;
public ViewHolder(View view) {
super(view);
view.setOnCreateContextMenuListener(this);
title = (TextView) view.findViewById(R.id.title);
desc = (TextView) view.findViewById(R.id.desc);
}
}
@Override
public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_layout, parent, false);
ViewHolder vh = new ViewHolder((LinearLayout) view);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Message item = mDataset.get(position);
holder.title.setText(item.getTitle());
holder.desc.setText(item.getDesc());
int numImages = item.getImages().size();
if (numImages == 1) {
// Inflate images_one.xml into main_layout.xml
} else if (numImages == 2) {
// Inflate images_two.xml into main_layout.xml
} else if (numImages == 3) {
// Inflate images_three.xml into main_layout.xml
}
// ETC...
}
@Override
public int getItemCount() {
return mDataset.size();
}
}
实现它的最佳方法是什么?
Android 打算使用 ViewHolder
的方式是让视图在 onCreateViewHolder()
中成为 created/inflated,然后在 onBindViewHolder()
中填充适配器数据。这很重要,因为 onCreateViewHolder()
仅在没有可回收的东西时才被调用。
与ListView
不同,RecyclerView
实际上是在回收ViewHolder
s而不是View
s。因此,尝试在 onBindViewHolder()
中增加视图是行不通的。
注意到onCreateViewHolder()
中的viewType
参数了吗?这就是关键。
首先,您需要在适配器中覆盖 getItemViewType()
。让我们在这里走捷径;由于视图类型是 int
,我们只使用项目中的图像数作为视图类型:
@Override
public int getItemViewType(int position) {
// ... return the number of images for data at position
}
(通常我会定义 final int
像 VIEW_TYPE_ONE_IMAGE
等来 returned,这是正确的做法。)
适配器如何使用视图类型?如果位置 0 处的数据的视图类型为 4,则 RecyclerView
只会将在 onCreateViewHolder()
中使用 viewType == 4
创建的 ViewHolder
传递给 onBindViewHolder()
使用 position == 0
.
现在你的 onCreateViewHolder()
可能看起来像这样:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_layout, parent, false);
return new ViewHolder(view, viewType);
}
(仅供参考:onCreateViewHolder()
的 return 类型应该是 你的 ViewHolder
类型,而不是 RecyclerView
s。)
请注意,我向您的 ViewHolder
构造函数添加了一个 viewType
参数:
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener, View.OnClickListener {
public TextView title;
public TextView desc;
public ImageView img1;
public ImageView img2;
public ViewHolder(View view, int viewType) {
super(view);
view.setOnCreateContextMenuListener(this);
title = (TextView) view.findViewById(R.id.title);
desc = (TextView) view.findViewById(R.id.desc);
ViewGroup placeholder = (ViewGroup) view.findViewById(R.id.placeholder);
// here you can use the viewType to help you figure
// out which child views you need references for
switch (viewType) {
case 1:
// ...
break;
case 2:
LayoutInflater.from(parent.getContext()).inflate(R.layout.images_two, placeholder); // this combines inflate() and addView()
img1 = placeholder.findViewById(R.id.image_one);
img2 = placeholder.findViewById(R.id.image_two);
break;
// ...
}
}
}
如果您在所有不同的视图中使用相同的 ID,则不一定需要在构造函数中使用 viewType
;您可以在 onBindViewHolder()
.
中检查它们是否为空
如果您有许多不同的视图类型,组织 create/bind 代码的一种好方法是使您的 ViewHolder
子类抽象,进一步将其子类化为每种视图类型的不同类型,并且然后 return onCreateViewHolder()
中适当类型的实例。在你的抽象类型中声明一个像 public void bind(YourItemType item)
这样的抽象方法并在你的子类中实现,那么你的 onBindViewHolder()
方法就是:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(data.get(position)); // ... or however you access an item at position
}
...创建看起来像:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
switch (viewType) {
case 1:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.images_one, parent, false);
return new OneImageViewHolder(view);
case 2:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.images_two, parent, false);
return new TwoImageViewHolder(view);
// ... etc. etc.
}
}
别忘了,您可能会在 onBindViewHolder()
中传递一个回收的 ViewHolder
,因此您必须假设所有视图都是 "dirty" 并将每个子视图设置为position
.
处数据的已知状态
我有一个 RecyclerView 适配器。在 onBindViewHolder
中,我检查传递给适配器的每个项目有多少图像。根据图像的数量,我想 load/inflate 一个子布局到主布局中。
例如:
- 如果项目有 1 张图片,我需要将
images_one.xml
膨胀到主布局中 - 如果项目有 2 张图片,我需要将
images_two.xml
膨胀到主布局中 - 等等,最多 8 张图片 (
images_eight.xml
)
这里是主要布局,main_layout.xml
:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
// INFLATE CHILD LAYOUT HERE (images_one.xml, images_two.xml, etc.)
<TextView
android:id="@+id/desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
这是需要膨胀到主布局中的子布局之一,images_two.xml
:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/image_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/image_two"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
最后,这是我的 RecyclerView 适配器:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private static Context context;
private List<Message> mDataset;
public RecyclerAdapter(Context context, List<Message> myDataset) {
this.context = context;
this.mDataset = myDataset;
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener, View.OnClickListener {
public TextView title;
public TextView desc;
public ViewHolder(View view) {
super(view);
view.setOnCreateContextMenuListener(this);
title = (TextView) view.findViewById(R.id.title);
desc = (TextView) view.findViewById(R.id.desc);
}
}
@Override
public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_layout, parent, false);
ViewHolder vh = new ViewHolder((LinearLayout) view);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Message item = mDataset.get(position);
holder.title.setText(item.getTitle());
holder.desc.setText(item.getDesc());
int numImages = item.getImages().size();
if (numImages == 1) {
// Inflate images_one.xml into main_layout.xml
} else if (numImages == 2) {
// Inflate images_two.xml into main_layout.xml
} else if (numImages == 3) {
// Inflate images_three.xml into main_layout.xml
}
// ETC...
}
@Override
public int getItemCount() {
return mDataset.size();
}
}
实现它的最佳方法是什么?
Android 打算使用 ViewHolder
的方式是让视图在 onCreateViewHolder()
中成为 created/inflated,然后在 onBindViewHolder()
中填充适配器数据。这很重要,因为 onCreateViewHolder()
仅在没有可回收的东西时才被调用。
与ListView
不同,RecyclerView
实际上是在回收ViewHolder
s而不是View
s。因此,尝试在 onBindViewHolder()
中增加视图是行不通的。
注意到onCreateViewHolder()
中的viewType
参数了吗?这就是关键。
首先,您需要在适配器中覆盖 getItemViewType()
。让我们在这里走捷径;由于视图类型是 int
,我们只使用项目中的图像数作为视图类型:
@Override
public int getItemViewType(int position) {
// ... return the number of images for data at position
}
(通常我会定义 final int
像 VIEW_TYPE_ONE_IMAGE
等来 returned,这是正确的做法。)
适配器如何使用视图类型?如果位置 0 处的数据的视图类型为 4,则 RecyclerView
只会将在 onCreateViewHolder()
中使用 viewType == 4
创建的 ViewHolder
传递给 onBindViewHolder()
使用 position == 0
.
现在你的 onCreateViewHolder()
可能看起来像这样:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_layout, parent, false);
return new ViewHolder(view, viewType);
}
(仅供参考:onCreateViewHolder()
的 return 类型应该是 你的 ViewHolder
类型,而不是 RecyclerView
s。)
请注意,我向您的 ViewHolder
构造函数添加了一个 viewType
参数:
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener, View.OnClickListener {
public TextView title;
public TextView desc;
public ImageView img1;
public ImageView img2;
public ViewHolder(View view, int viewType) {
super(view);
view.setOnCreateContextMenuListener(this);
title = (TextView) view.findViewById(R.id.title);
desc = (TextView) view.findViewById(R.id.desc);
ViewGroup placeholder = (ViewGroup) view.findViewById(R.id.placeholder);
// here you can use the viewType to help you figure
// out which child views you need references for
switch (viewType) {
case 1:
// ...
break;
case 2:
LayoutInflater.from(parent.getContext()).inflate(R.layout.images_two, placeholder); // this combines inflate() and addView()
img1 = placeholder.findViewById(R.id.image_one);
img2 = placeholder.findViewById(R.id.image_two);
break;
// ...
}
}
}
如果您在所有不同的视图中使用相同的 ID,则不一定需要在构造函数中使用 viewType
;您可以在 onBindViewHolder()
.
如果您有许多不同的视图类型,组织 create/bind 代码的一种好方法是使您的 ViewHolder
子类抽象,进一步将其子类化为每种视图类型的不同类型,并且然后 return onCreateViewHolder()
中适当类型的实例。在你的抽象类型中声明一个像 public void bind(YourItemType item)
这样的抽象方法并在你的子类中实现,那么你的 onBindViewHolder()
方法就是:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(data.get(position)); // ... or however you access an item at position
}
...创建看起来像:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
switch (viewType) {
case 1:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.images_one, parent, false);
return new OneImageViewHolder(view);
case 2:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.images_two, parent, false);
return new TwoImageViewHolder(view);
// ... etc. etc.
}
}
别忘了,您可能会在 onBindViewHolder()
中传递一个回收的 ViewHolder
,因此您必须假设所有视图都是 "dirty" 并将每个子视图设置为position
.