多级RecyclerView嵌套下的下载进度刷新方案
- 最近在做一个类似于应用市场的项目,其中首页用到的嵌套RecyclerView,由于涉及到下载进度刷新的问题,在此记录一下
原理简介
其实对于这种嵌套的RecyclerView中的进度刷新问题,其最本质的问题是找到需要刷新的控件然后把进度刷上去,根据这个思路我们就有了以下的一些代码。实现
首先一般针对稍微复杂的布局,我们可以通过RecyclerView.Adapter中的getItemViewType来实现,该方法的返回值即onCreateViewHolder回调方法中的viewType
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {}
这里不做过多的赘述;
下面到重点了,假设我现在有一个观察者(该观察者是需要刷新进度的UI,既可以是Activity也可以是Fragment)观察到下载进度变化了,它大约是这样的
DataWatcher mDownloadObserver = new DataWatcher() {
@Override
public void notifyUpdate(DownloadInfo downloadInfo) {
//此处处理下载进度刷新的逻辑
}
};
这个观察者中包含了一个嵌套的RecyclerView.那我的思路是先通过外层RecyclerView的LayoutManager拿到最外层的在屏幕上的可见的Item.
当拿到Item时,我们去遍历这些Item,通过ItemView绑定Tag的方式拿到其对应的ViewHolder.
这里我通过定义一个BaseViewHolder的方式,让每一个类型的Holder都有其对应的类型,然后再通过其暴露出去的获取类型接口来知道这个Item到底是哪种类型的Item然后将其强转成具体的类型
public class BaseViewHolder extends RecyclerView.ViewHolder {
private int mType;
public int getType() {
return mType;
}
/**
*这里的type用来标识该ViewHolder的具体类型,便于在UI中遍历所有ItemView的时候可以将其强转成具体的类型
*/
public BaseViewHolder(View itemView,int type) {
super(itemView);
/*setTag的目的是为了将ViewHolder和ItemView绑定起来,便于遍历ItemView的时候拿到其对应的ViewHolder*/
itemView.setTag(this);
this.mType = type;
}
}
遍历所有可见的Item,并根据Type将其强转成对应的类型
int count = mLayoutManager.getChildCount();
for (int k = 0; k < count; k++) {
View itemView = mLayoutManager.getChildAt(k);
if (itemView != null && itemView.getTag() != null ) {
BaseViewHolder viewHolder = (BaseViewHolder) itemView.getTag();
switch (viewHolder.getType()) {
case ChoicenessItemType.VERTICAL_LIST:
handleVerticalListUpdate(downloadInfo, (VerticalListVH) viewHolder);
break;
case ChoicenessItemType.GRID:
handleGridUpdate(downloadInfo, (GridVH) viewHolder);
break;
case ChoicenessItemType.HORIZONTAL_LIST:
handleHorizontalUpdate(downloadInfo, (HorizontalListVH) viewHolder);
break;
default:
break;
}
}
}
在完成上述步骤以后,我们基本已经将工作完成了一大半,现在我们只需要再通过内部被嵌套的子RecyclerView的LayoutManager拿到其所有显示在屏幕上的的Item,然后依旧通过给子ItemView绑定tag的方式来找到我们真正需要刷新的控件。这里有很多种方式来实现,我这里讲一下我在项目中的做法,因为我们下载的时候会回调被下载的应用信息过来,其中有包名这一项,那么我就可以通过RecyclerView在创建视图的时候将应用信息和itemView通过setTag的方式绑定,具体操作是在RecyclerView中的onBindViewHolder中绑定,然后当观察到进度变化时,判断包名相等的即我们所需要的控件,
@Override
public void onBindViewHolder(@NonNull final VerticalListSubVH holder, int position) {
if (holder == null) {
return;
}
//这里的getItem拿到的就是应用信息DownloadInfo
holder.downloadBtn.setTag(getItem(position));
}
private void handleVerticalListUpdate(DownloadInfo downloadInfo, VerticalListVH verticalListVH) {
RecyclerView.LayoutManager layoutManager = verticalListVH.gameListRv.getLayoutManager();
int size = layoutManager.getChildCount();
for (int i = 0; i < size; i++) {
View view = layoutManager.getChildAt(i);
VerticalListSubVH verticalListSubVH = (VerticalListSubVH) view.getTag();
if (verticalListSubVH != null && verticalListSubVH.downloadBtn.getTag() != null) {
DownloadInfo tag = (DownloadInfo)verticalListSubVH.downloadBtn.getTag();
if (TextUtils.equals(downloadInfo.getPackageName(), tag.getPackageName())) {
long progress = downloadInfo.getProgress();
long fileLength = downloadInfo.getFileLength();
int p = (int) (progress * 100 / fileLength);
//这里的p即需要刷新的进度
//verticalListSubVH.downloadBtn 该控件即我们要找的需要刷新的控件
}
}
}
}
}
- Tips
最后有个需要注意的地方,我们需要在每个RecyclerView中的onBindViewHolder方法中去初始化按钮的状态,比如当前是下载中还是暂停或者安装等