旧设备RecyclerView多视图绑定卡顿,求高效优化方案
针对你遇到的2015年Galaxy A5(API23)上RecyclerView绑定数据卡顿问题,结合你用Folding Cell的场景,我整理了几个针对性的优化思路,帮你把单次绑定耗时降到16ms以内:
1. 延迟展开状态视图的绑定,只在需要时处理
折叠状态下用户看不到展开后的视图,完全没必要在onBindViewHolder里提前绑定这些视图。可以把展开状态的绑定逻辑移到FoldingCell的展开事件回调里,只有当用户主动点击展开时才执行:
// 在ViewHolder的构造或bind方法里设置展开监听 holder.cell.setOnClickListener(v -> { if (holder.cell.isUnfolded()) { holder.cell.fold(true); unfoldedIndexes.remove(holder.getLayoutPosition()); } else { holder.cell.unfold(true); unfoldedIndexes.add(holder.getLayoutPosition()); // 此时才绑定展开状态的视图 bindUnfoldedState(holder, holder.getLayoutPosition()); } });
这样滚动时只需要处理折叠状态的少量视图,单次绑定的工作量直接减半。
2. 预格式化数据,避免主线程实时计算
你当前在绑定阶段做的字符串格式化、类型转换,都是旧设备上的耗时大户:
- 把
context.getString(R.string.currency, customLayout.getP())这种格式化操作,提前在数据从BaaS拉取后就完成,把结果存在CustomLayout的字段里(比如formattedCurrency),绑定的时候直接holder.currency.setText(customLayout.getFormattedCurrency()); - 把
holder.total.setText(""+customLayout.getTotal())改成提前将数字转成字符串存在模型中,或者用String.valueOf()替代字符串拼接,避免每次绑定都做类型转换。
3. 懒加载展开状态的视图,减少ViewHolder初始化耗时
你现在在onCreateViewHolder里一次性初始化了所有视图,包括折叠状态下用不到的展开视图。可以对这些视图做懒加载,只有第一次需要时才初始化:
public class ViewHolder extends RecyclerView.ViewHolder { private final FoldingCell cell; // 折叠状态视图直接初始化 public final TextView currency; public final TextView add; // 展开状态视图懒加载 private TextView tText; private TextView fText; public ViewHolder(FoldingCell cell) { super(cell); this.cell = cell; // 只初始化折叠状态视图 currency = cell.findViewById(R.id.currency); add = cell.findViewById(R.id.add); } // 展开状态视图通过getter懒加载 public TextView getTText() { if (tText == null) { tText = cell.findViewById(R.id.tText); } return tText; } public TextView getFText() { if (fText == null) { fText = cell.findViewById(R.id.fText); } return fText; } }
这样onCreateViewHolder的初始化耗时会大幅降低,滚动时视图复用的成本也更低。
4. 避免不必要的状态切换与布局重绘
你当前在onBindViewHolder里不管当前cell状态如何,都强制调用unfold(true)或fold(true),这会触发不必要的布局重绘。可以加个状态判断,只有当当前状态和目标状态不一致时才执行操作:
@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { boolean shouldUnfold = unfoldedIndexes.contains(position); if (shouldUnfold && !holder.cell.isUnfolded()) { holder.cell.unfold(true); bindUnfoldedState(holder, position); } else if (!shouldUnfold && holder.cell.isUnfolded()) { holder.cell.fold(true); bindFoldedState(holder, position); } else { // 状态一致,只绑定当前状态的必要数据(避免滚动时重复绑定) if (shouldUnfold) { bindUnfoldedState(holder, position); } else { bindFoldedState(holder, position); } } }
5. 提前处理静态UI状态,避免主线程计算
比如holder.tText.setTextColor(Color.parseColor("#6F90CF")),可以提前把颜色值转成int常量(比如private static final int T_TEXT_COLOR = Color.parseColor("#6F90CF")),或者存在数据模型里,绑定的时候直接用常量赋值,避免每次都解析颜色字符串。
这些方案可以组合使用,建议先从预格式化数据和避免不必要的状态切换入手,这两个改动最小但见效最快,之后再逐步引入懒加载和延迟绑定,应该能明显改善旧设备上的滚动流畅度。
内容的提问来源于stack exchange,提问作者bcsta

