前言
在寫(xiě)這個(gè)之前呢,我也百度(谷歌需要梯子)了一波,在github上也找了一番,實(shí)現(xiàn)的方式很多,還有很多封裝好的庫(kù)可以直接使用。鄙人有個(gè)習(xí)慣,就是很多東西都喜歡使用輕量級(jí)的,所以很多庫(kù)的功能太全了,不符合鄙人的習(xí)慣。微信朋友朋友圈圖片的九宮格效果并不難,尤其是RecyclerView如此強(qiáng)大的情況下,所以鄙人決定自己擼一個(gè),不求功能多全面,滿(mǎn)足自己的項(xiàng)目需求就好,于是就有了此文。
正文
沒(méi)圖說(shuō)個(gè)雞兒:

先照著朋友圈分析一下列表數(shù)據(jù)(只分析圖片,視頻跟單圖其實(shí)是差不多的)的大概樣子:
- 頭部下拉刷新(這個(gè)效果不在本文的考慮范圍內(nèi));
- 列表的圖片大概分為單張,兩列4張,三列N張;
- 最多可以顯示9張;
- 單張圖片自適應(yīng)高度,寬度最大為列表寬度;
- 多張圖片為正方形圖片,田字格或者九宮格。
所以我們可以把列表看做是三種viewType的集合,于是乎,我們的getViewType重寫(xiě)如下:
int size ; // size的值是列表圖片數(shù)量
if (size < 2) {
//沒(méi)有圖片或者單張圖片
return 1;
} else if (size == 4) {
//四張圖片
return 2;
} else {
//其他情況
return 3;
}
在onCreateViewHolder方法中,我們需要去創(chuàng)建我們的holder。其實(shí)四張圖片的田字格可以當(dāng)做是九宮格的一種特殊形式,當(dāng)然一張圖片也可以當(dāng)做是九宮格的一種特殊形式,但鄙人將一張圖片的情況跟九宮格的分開(kāi),那么我們就需要兩個(gè)不同的holder去分別承載一張圖和多張圖的情況。多張圖的情況,里面圖片的列表我還是使用RecylcerView(RV簡(jiǎn)直不要太好用)。
對(duì)于總體的item來(lái)說(shuō),除了圖片展示部分,其他的部分都是一樣的,我們可以創(chuàng)建一個(gè)父類(lèi)的holder,父類(lèi)的holder提供了一個(gè)抽象的方法loadSelf ()提供給子類(lèi)的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);
}
根據(jù)上面的分析,我們還需要?jiǎng)?chuàng)建兩個(gè)子類(lèi)的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 ; //圖片的數(shù)組
// 如果沒(méi)有圖片,那就是只有文字,隱藏圖片控件
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);
這一句不要忘了,不然會(huì)出現(xiàn)item閃爍的現(xiàn)象。
holder創(chuàng)建好了,下面就是onBindViewHolder()方法里面的代碼了。由于上面封裝的holder里面提供了setData()的方法,所以onBindViewHolder()方法里面的內(nèi)容就簡(jiǎn)單多了:
@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點(diǎn)擊事件
}
});
}
}
核心其實(shí)就調(diào)用holder的setData()方法和添加itemView的點(diǎn)擊事件。
前面有講到九宮格的情況下,圖片展示的是方圖,田字格是九宮格的一種特殊情況,上面OtherHolder 的構(gòu)造方法里面需要傳入圖片格子的column,所以圖片RecyclerView的寬度是wrap_content,那么我們需要手動(dòng)的去實(shí)際計(jì)算一下圖片的寬度。這個(gè)老鐵們根據(jù)實(shí)際情況去計(jì)算就可以了。
另外還有一些交互的功能需要暴露出去,老鐵們根據(jù)實(shí)際情況添加自己的接口就好了,比如我的:
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);
}
到此效果就實(shí)現(xiàn)完了。