前言
在写这个之前呢,我也百度(谷歌需要梯子)了一波,在github上也找了一番,实现的方式很多,还有很多封装好的库可以直接使用。鄙人有个习惯,就是很多东西都喜欢使用轻量级的,所以很多库的功能太全了,不符合鄙人的习惯。微信朋友朋友圈图片的九宫格效果并不难,尤其是RecyclerView如此强大的情况下,所以鄙人决定自己撸一个,不求功能多全面,满足自己的项目需求就好,于是就有了此文。
正文
没图说个鸡儿:
先照着朋友圈分析一下列表数据(只分析图片,视频跟单图其实是差不多的)的大概样子:
- 头部下拉刷新(这个效果不在本文的考虑范围内);
- 列表的图片大概分为单张,两列4张,三列N张;
- 最多可以显示9张;
- 单张图片自适应高度,宽度最大为列表宽度;
- 多张图片为正方形图片,田字格或者九宫格。
所以我们可以把列表看做是三种viewType的集合,于是乎,我们的getViewType重写如下:
int size ; // size的值是列表图片数量
if (size < 2) {
//没有图片或者单张图片
return 1;
} else if (size == 4) {
//四张图片
return 2;
} else {
//其他情况
return 3;
}
在onCreateViewHolder方法中,我们需要去创建我们的holder。其实四张图片的田字格可以当做是九宫格的一种特殊形式,当然一张图片也可以当做是九宫格的一种特殊形式,但鄙人将一张图片的情况跟九宫格的分开,那么我们就需要两个不同的holder去分别承载一张图和多张图的情况。多张图的情况,里面图片的列表我还是使用RecylcerView(RV简直不要太好用)。
对于总体的item来说,除了图片展示部分,其他的部分都是一样的,我们可以创建一个父类的holder,父类的holder提供了一个抽象的方法loadSelf ()提供给子类的holder去处理自己的逻辑。
abstract class ItemHolder extends RecyclerView.ViewHolder {
public ItemHolder(View itemView) {
super(itemView);
}
private void setData(final int position, final CircleListModel model) {
//todo 公共的逻辑
loadSelf(position, model);
}
public abstract void loadSelf(final int position, final CircleListModel model);
}
根据上面的分析,我们还需要创建两个子类的holder。
- 提供给单张图片使用的:
private class OneHolder extends ItemHolder {
AppCompatImageView ivCover;
public OneHolder(View itemView) {
super(itemView);
ivCover = itemView.findViewById(R.id.iv_cover);
}
@Override
public void loadSelf(int position, CircleListModel model) {
List<> mediaList ; //图片的数组
// 如果没有图片,那就是只有文字,隐藏图片控件
if (mediaList == null || mediaList.isEmpty()) {
ivCover.setVisibility(View.GONE);
} else {
ivCover.setVisibility(View.VISIBLE);
//todo 显示图片
}
}
- 提供给多张使用的:
private class OtherHolder extends ItemHolder {
RecyclerView rvItems;
RAdapter<CircleListModel.CircleMsgMediaList> mAdapter;
private ArrayList<CircleListModel.CircleMsgMediaList> mStrings = new ArrayList<>();
public OtherHolder(final View itemView, int columns) {
super(itemView);
rvItems = itemView.findViewById(R.id.rv_items);
mAdapter = new RAdapter<CircleListModel.CircleMsgMediaList>(mContext, R.layout.other_item, mStrings) {
@Override
protected void init(RViewHolder holder, final CircleListModel.CircleMsgMediaList circleMsgMediaList) {
LinearLayoutCompat.LayoutParams layoutParams = new LinearLayoutCompat.LayoutParams(widthPixels, widthPixels);
final AppCompatImageView image = holder.getView(R.id.image);
image.setLayoutParams(layoutParams);
//todo 加载图片
});
}
};
GridLayoutManager manager = new GridLayoutManager(mContext, columns);
rvItems.setLayoutManager(manager);
rvItems.setAdapter(mAdapter);
rvItems.addItemDecoration(new GridSpacingItemDecoration(columns, SPACE, false));
rvItems.setHasFixedSize(true);
((SimpleItemAnimator) rvItems.getItemAnimator()).setSupportsChangeAnimations(false);
}
public void loadSelf(final int position, final CircleListModel model) {
mStrings.clear();
mStrings.addAll(model.getCircleMsgMediaList());
mAdapter.notifyDataSetChanged();
}
}
上面的代码涉及到我自己封装Adapter和添加分割线的方法。老铁可以自己灵活的去换一下。
((SimpleItemAnimator) rvItems.getItemAnimator()).setSupportsChangeAnimations(false);
这一句不要忘了,不然会出现item闪烁的现象。
holder创建好了,下面就是onBindViewHolder()方法里面的代码了。由于上面封装的holder里面提供了setData()的方法,所以onBindViewHolder()方法里面的内容就简单多了:
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int p) {
final int position = holder.getAdapterPosition();
final CircleListModel model = mList.get(position);
((ItemHolder) holder).setData(position, model);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//todo item点击事件
}
});
}
}
核心其实就调用holder的setData()方法和添加itemView的点击事件。
前面有讲到九宫格的情况下,图片展示的是方图,田字格是九宫格的一种特殊情况,上面OtherHolder 的构造方法里面需要传入图片格子的column,所以图片RecyclerView的宽度是wrap_content,那么我们需要手动的去实际计算一下图片的宽度。这个老铁们根据实际情况去计算就可以了。
另外还有一些交互的功能需要暴露出去,老铁们根据实际情况添加自己的接口就好了,比如我的:
public interface OnItemListener {
void onItemClick(int position, CircleListModel t);
void showComments(int position, CircleListModel t);
void delete(int position, CircleListModel t);
void onLinkClick(String url);
}
到此效果就实现完了。