通常,我们在展示列表时,列表中的数据类型是多种类型的,而较多时候,多种数据类型包括两级结构,一级是公共部分,包括头像、标题、时间等,二级是具体的内容部分,内容可能是图片、视频、九宫格或者横向列表等。在使用RecyclerView展示时,传统的做法是每个数据类型Type写一个布局,然后根据不同Type写不同ViewHolder,这种写法重复的代码太多,而且比如要是修改标题title,则就要在每个ViewHolder中修改代码,工作量较大。另外一种做法是,将公共部分抽出来写一个公共的BaseContentViewHolder,然后其他具体的内容ViewHolder继承BaseContentViewHolder,这种做法能够解决大量重复的代码问题,但是每个item的布局都要写公共部分的布局,仍旧很不优雅。本文将在前述两种方案的基础上加以改进,给出一种更优雅的方法,本文的关键是通过使用FrameLayout将二级内容部分动态加载,所以当有新类型数据时,每次只需要编写二级内容相关代码即可。
(一)公共部分布局base_item_layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="#F1F0F0"
/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="14dp"
android:layout_marginRight="14dp"
android:layout_marginTop="10dp"
>
<RelativeLayout
android:id="@+id/head_rl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
//一级头像
<ImageView
android:id="@+id/head_imageView"
style="@style/MyFollowUserImageMode"
android:src="@mipmap/ic_avatar_default_in_profile"
/>
//一级用户名
<TextView
android:id="@+id/user_name_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/head_imageView"
android:text=""
android:layout_marginLeft="5dp"
android:textColor="@color/swipe_night"
android:textSize="15sp"
android:includeFontPadding="false"
/>
//一级时间
<TextView
android:id="@+id/date_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:textSize="13sp"
android:textColor="@color/b3"
android:text=""
android:layout_below="@id/user_name_tv"
android:layout_toRightOf="@id/head_imageView"
android:includeFontPadding="false"
/>
</RelativeLayout>
//一级标题
<TextView
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="15sp"
android:layout_marginTop="8dp"
android:textColor="#398CE7"
android:maxLines="1"
android:ellipsize="end"
/>
//加载二级内容
<FrameLayout
android:id="@+id/content_fl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
>
</FrameLayout>
</LinearLayout>
</LinearLayout>
公共部分的布局包括头像、用户名、时间、标题等一级内容以及加载具体不同数据的一个FrameLayout,FrameLayout是加载具体不同数据的,如图片、视频、九宫格、横向列表等。本文假设需要加载的数据类型包括图片、视频、显示图片的九宫格。
(二)二级内容布局
图片布局:image_item_layout:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_img_iv"
android:layout_width="match_parent"
android:layout_height="200dp"
>
</ImageView>
视频布局:video_item_layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/video_bg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="fitXY" />
<ImageView
android:id="@+id/play_iv"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_centerInParent="true"
android:src="@mipmap/ic_media_play_in_list" />
</RelativeLayout>
视频布局截图
九宫格布局:gridview_item_layout:
<?xml version="1.0" encoding="utf-8"?>
<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/grid_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="3"
android:horizontalSpacing="7dp"
android:verticalSpacing="7dp" >
</GridView>
这里使用的是GridView控件,也可以使用RecyclerView。
(三)适配器Adapter:
本方案的核心是适配器部分,所以只贴出适配器的核心代码以及其他一些核心代码,其他不影响阅读及理解的代码将不贴出来。
以下BaseSuperRecyclerViewAdapter是继承自RecyclerView.Adapter的基类Adapter,不影响阅读及理解,将不贴出BaseSuperRecyclerViewAdapter的代码,AdviseInfoEntity是实体数据类,将不贴出代码。
public class AdviseListAdatper extends BaseSuperRecyclerViewAdapter<AdviseInfoEntity> {
public AdviseListAdatper(@NonNull SuperRecyclerView superRecyclerView) {
super(superRecyclerView);
}
//对应 getItemViewType(int position)方法
@Override
public int getMyItemViewType(int position) {
AdviseInfoEntity adviseInfoEntity = getDataList().get(position);
return adviseInfoEntity.getTypeContent();
}
//对应 onCreateViewHolder(ViewGroup parent, int viewType)方法
@Override
public FrameViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType) {
View root = LayoutInflater.from(getContext()).inflate(R.layout.base_item_layout, parent, false);
ContentHolder subViewHolder = null;
switch (viewType){
//视频类型
case AdviseInfoEntity.VIDEO_CONTENT_TYPE:
subViewHolder = new VideoViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.video_item_layout, parent, false));
break;
//九宫格类型
subViewHolder = new GridViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.gridview_item_layout, parent, false));
break;
//图片类型
case AdviseInfoEntity.PIC_CONTENT_SINGLE_TYPE:
subViewHolder = new ImageViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.image_item_layout, parent, false));
break;
}
return new FrameViewHolder(root,subViewHolder);
}
@Override
public void onBindMyViewHolder(BaseRecyclerViewHolder holder, int position) {
FrameViewHolder frameViewHolder = (FrameViewHolder)holder;
AdviseInfoEntity data = getDataList().get(position);
//ImageLoader是封装的glide,不展示代码;ic_avatar_default是缺省图
ImageLoader.getInstance().loadCircleImage(getContext(), data.getAvatar(), frameViewHolder.headImageView, R.mipmap.ic_avatar_default, R.mipmap.ic_avatar_default);
frameViewHolder.userNameTv.setText(data.getRelateName());
frameViewHolder.dateTv.setText(DateUtil.stamp2Date(data.getCreateTime()));
if (TextUtils.isEmpty(data.getTitle())){
frameViewHolder.titleTv.setVisibility(View.GONE);
}else {
frameViewHolder.titleTv.setVisibility(View.VISIBLE);
frameViewHolder.titleTv.setText(data.getTitle());
}
//有视频
if (data.getTypeContent() == AdviseInfoEntity.VIDEO_CONTENT_TYPE){
((VideoViewHolder)frameViewHolder.subViewHolder).show(getContext(),data,position);
} else if (data.getTypeContent() == AdviseInfoEntity.PIC_LIST_CONTENT_TYPE){
//九宫格
((GridViewHolder)frameViewHolder.subViewHolder).show(getContext(),data,position);
}else if (data.getTypeContent() == AdviseInfoEntity.PIC_CONTENT_SINGLE_TYPE){
//单图
((ImageViewHolder)frameViewHolder.subViewHolder).show(getContext(),data,position);
}
}
public static class FrameViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.head_imageView)
ImageView headImageView;
@BindView(R.id.user_name_tv)
TextView userNameTv;
@BindView(R.id.date_tv)
TextView dateTv;
@BindView(R.id.head_rl)
RelativeLayout headRl;
@BindView(R.id.title_tv)
TextView titleTv;
@BindView(R.id.content_fl)
FrameLayout contentFl;
private ContentHolder subViewHolder;
public FrameViewHolder(View itemView, ContentHolder subViewHolder) {
super(itemView);
if (null!=subViewHolder){
contentFl.setVisibility(View.VISIBLE);
//将二级视图动态加载
contentFl.addView(subViewHolder.itemView);
this.subViewHolder = subViewHolder;
this.subViewHolder.frameHolder = subViewHolder.frameHolder;
}else {
contentFl.setVisibility(View.GONE);
}
}
}
}
BaseRecyclerViewHolder代码:
public abstract class BaseRecyclerViewHolder extends RecyclerView.ViewHolder {
public BaseRecyclerViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
ContentHolder是每个二级视图ViewHolder的基类
public class ContentHolder {
public AdviseListAdatper.FrameViewHolder frameHolder;
public final View itemView;
public ContentHolder(@NonNull final View itemView) {
this.itemView = itemView;
}
public @NonNull AdviseListAdatper.FrameViewHolder getParent() {
return frameHolder;
}
public final int getAdapterPosition() {
return getParent().getAdapterPosition();
}
public final int getLayoutPosition() {
return getParent().getLayoutPosition();
}
public final int getOldPosition() {
return getParent().getOldPosition();
}
public final boolean isRecyclable() {
return getParent().isRecyclable();
}
public final void setIsRecyclable(boolean recyclable) {
getParent().setIsRecyclable(recyclable);
}
}
(四)二级视图ViewHolder
(1)视频视图VideoViewHolder代码:
public class VideoViewHolder extends ContentHolder {
private ImageView imageView;
private ImageView playIv;
public VideoViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.video_bg);
playIv = itemView.findViewById(R.id.play_iv);
}
public void show(Context context, AdviseInfoEntity data, final int position) {
ImageLoader.getInstance().loadRoundImage16_9(context, data.getImg_url_load(), imageView, (int) PixelUtil.dp2px(0));
}
}
(2)九宫格视图GridViewHolder代码:
public class GridViewHolder extends ContentHolder{
//ImageGridViewAdapter是继承自系统的BaseAdapter,不展示代码
private GridView mGridView;
private ImageGridViewAdapter mAdapter;
public GridViewHolder(@NonNull View itemView) {
super(itemView);
mGridView = itemView.findViewById(R.id.grid_view);
}
public void show(Context context, AdviseInfoEntity data, final int position) {
mAdapter = new ImageGridViewAdapter(context,data.getPicList());
mGridView.setAdapter(mAdapter);
}
}
(3)图片视图ImageViewHolder代码:
public class ImageViewHolder extends ContentHolder {
private ImageView imageIv;
public ImageViewHolder(@NonNull View itemView) {
super(itemView);
imageIv = itemView.findViewById(R.id.item_img_iv);
}
public void show(Context context, AdviseInfoEntity data, final int position) {
//TopicImage是数据实体类,不影响阅读和理解不展示代码
TopicImage image = data.getPicList().get(0);
ImageLoader.getInstance().loadAsBitmapImage2(context, image.getPic_url(), singeIv);
}
}
(五)使用:
本文的核心是适配器AdviseListAdatper部分,与一般的列表加载适配器使用无异,只需要将数据list传进适配器即可,因此本文不给出具体的使用代码,后续会贴出demo的链接。