Android RecyclerView设置数据后获取高度并重设高度实现方法
实现方案
直接在adapter.notifyDataSetChanged()调用后同步获取RecyclerView高度必然不准——适配器刷新触发的测量、布局流程是加入主线程消息队列异步执行的,同步执行的取高度逻辑跑在布局流程前面,拿到的永远是你初始设置的固定高度值。
根据你的场景选下面两种实现即可:
方式1:布局完成后直接取渲染高度(全LayoutManager兼容)
不需要手动计算item高度,等RecyclerView完成自身排版后直接拿测量值,线性布局、网格布局、瀑布流布局都能用,写法最简单。
// 完成数据更新、刷新适配器 adapter.notifyDataSetChanged(); // post的任务会在RecyclerView布局完成后执行 recyclerView.post(() -> { // 此时拿到的是内容渲染后的真实测量高度 int realHeight = recyclerView.getMeasuredHeight(); // 重设控件高度 ViewGroup.LayoutParams params = recyclerView.getLayoutParams(); params.height = realHeight; recyclerView.setLayoutParams(params); });
注意:如果初始设置的固定高度小于内容总高度,RecyclerView会进入滚动模式,此时
getMeasuredHeight()拿到的还是初始固定高度,这种场景请用方式2。
方式2:手动累加计算内容总高度(精度最高,不受初始高度影响)
自己遍历所有item累加高度,算出来的是所有内容完全展开无滚动的总高度,不受初始固定高度、滚动状态影响,适合需要精准展开所有内容的场景。
线性布局(LinearLayoutManager)计算逻辑
adapter.notifyDataSetChanged(); recyclerView.post(() -> { LinearLayoutManager lm = (LinearLayoutManager) recyclerView.getLayoutManager(); int totalHeight = 0; int itemCount = adapter.getItemCount(); for (int i = 0; i < itemCount; i++) { View itemView = lm.findViewByPosition(i); if (itemView == null) continue; // 累加item本身高度+上下margin RecyclerView.LayoutParams itemLp = (RecyclerView.LayoutParams) itemView.getLayoutParams(); totalHeight += itemView.getMeasuredHeight() + itemLp.topMargin + itemLp.bottomMargin; } // 算上RecyclerView自身的上下内边距 totalHeight += recyclerView.getPaddingTop() + recyclerView.getPaddingBottom(); // 如果加了ItemDecoration,还要把对应decoration的高度加上 // 重设高度 ViewGroup.LayoutParams lp = recyclerView.getLayoutParams(); lp.height = totalHeight; recyclerView.setLayoutParams(lp); });
网格布局(GridLayoutManager)计算逻辑
网格布局同一行的item高度一致,不需要逐item累加,按行计算即可:
GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager(); int spanCount = glm.getSpanCount(); int totalHeight = 0; // 算总行数 int rowCount = (int) Math.ceil(adapter.getItemCount() * 1.0 / spanCount); for (int row = 0; row < rowCount; row++) { int firstPosOfRow = row * spanCount; View itemView = glm.findViewByPosition(firstPosOfRow); if (itemView == null) continue; RecyclerView.LayoutParams itemLp = (RecyclerView.LayoutParams) itemView.getLayoutParams(); totalHeight += itemView.getMeasuredHeight() + itemLp.topMargin + itemLp.bottomMargin; } totalHeight += recyclerView.getPaddingTop() + recyclerView.getPaddingBottom(); // 重设高度逻辑和线性布局一致
避坑点
- 不推荐用
OnGlobalLayoutListener实现,除非你记得在拿到高度后立刻移除监听,否则会多次触发重复设置高度的逻辑,post()方法会自动在单次布局完成后执行,没有内存泄漏风险。 - 如果列表有头布局、尾布局、空布局,遍历计算高度的时候要把这些特殊item的高度一起算进去,不要遗漏。
- 如果给RecyclerView设置了
ItemDecoration,总高度要把decoration占用的高度也累加进去,否则最终高度会偏小。
内容的提问来源于stack exchange,提问作者PhongBM
相关产品推荐
相关产品推荐

