使用GridLayoutManager的RecyclerView首次加载Cell高度异常问题
问题分析与解决方案
这种首次加载布局异常、切换Tab后恢复的情况,核心原因通常是首次布局时RecyclerView或Cell的尺寸还未正确计算,再加上Picasso异步加载图片的特性,导致LinearLayout的weight布局计算出现偏差。咱们一步步来解决:
核心问题拆解
- RecyclerView尺寸未就绪:你的RecyclerView用了
0dp(match_constraint)依赖ConstraintLayout约束,首次初始化时它的宽度还没完成测量,此时你尝试计算的item宽度是不准确的。 - 图片异步加载影响布局:Picasso加载图片是异步的,首次加载时ImageView显示的是占位图
@drawable/icon,它的尺寸和服务器返回的实际图片可能不一致,导致LinearLayout基于weight分配的高度不符合预期。切换Tab后图片已缓存,ImageView有了正确尺寸,布局自然恢复正常。
具体修复步骤
1. 等RecyclerView布局完成后再初始化Adapter
在Fragment的onViewCreated方法中,监听RecyclerView的全局布局事件,确保它的尺寸确定后再计算item宽度并设置Adapter:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); RecyclerView recyclerView = view.findViewById(R.id.recyclerViewCategories); recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); // 监听RecyclerView布局完成事件 recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // 移除监听,避免重复触发 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { recyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } // 计算item宽度:RecyclerView宽度的1/2,减去左右margin(你的Cell有10dp的margin) int recyclerViewWidth = recyclerView.getWidth(); int itemWidth = (recyclerViewWidth - 2 * getResources().getDimensionPixelSize(R.dimen.cell_margin)) / 2; // 这里建议把10dp定义成dimen资源,方便统一修改 // 初始化Adapter并设置 List<MedicineCategoryDataModel> categoryData = ...; // 你的数据 CategoryRecyclerViewAdapter adapter = new CategoryRecyclerViewAdapter(getContext(), categoryData, itemWidth); recyclerView.setAdapter(adapter); } }); }
2. 在Adapter中正确设置Cell尺寸并监听图片加载完成
修改Adapter的代码,在onBindViewHolder中设置Cell的宽高,并在图片加载完成后刷新当前item,确保布局重新计算:
public class CategoryRecyclerViewAdapter extends RecyclerView.Adapter<CategoryRecyclerViewAdapter.ViewHolder> { // ... 其他成员变量 private int itemWidth; public CategoryRecyclerViewAdapter(Context context, List<MedicineCategoryDataModel> data, int width) { this.layoutInflater = LayoutInflater.from(context); this.categoryData = data; this.context = context; this.itemWidth = width; // 接收计算好的item宽度 } @Override public void onBindViewHolder(ViewHolder holder, int position) { MedicineCategoryDataModel item = categoryData.get(position); holder.textViewCategoryName.setText(item.categoryName); // 设置Cell的宽高(因为要正方形,高度等于宽度) ViewGroup.LayoutParams params = holder.itemView.getLayoutParams(); params.width = itemWidth; params.height = itemWidth; holder.itemView.setLayoutParams(params); // 加载图片并监听完成事件 String iconUrl = item.iconUrl; EPClientLayer.getInstance().getImageHandler().loadImage(context, iconUrl, holder.imageViewCategoryIcon, new Callback() { @Override public void onSuccess() { // 图片加载完成后,通知RecyclerView刷新当前item,让布局重新计算 notifyItemChanged(position); } @Override public void onError() { // 可选:处理图片加载失败的情况 } }); } // ... 其他代码 }
3. 优化Cell布局的小细节
- 把TextView的
textSize="15dp"改成15sp,sp是专门用于文字大小的单位,适配不同系统字体大小设置。 - 给ImageView添加
android:scaleType="centerCrop",避免图片拉伸变形,保证显示效果一致:
<ImageView android:id="@+id/categoryImage" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginLeft="40dp" android:layout_marginRight="40dp" android:layout_weight="0.8" android:scaleType="centerCrop" android:src="@drawable/icon" />
为什么之前的方法无效?
你之前尝试在onCreateViewHolder中设置尺寸,此时RecyclerView的parent布局还未完成测量,获取到的宽度是不准确的;直接用屏幕宽度计算没有考虑RecyclerView的margin和约束布局的影响,同样会导致尺寸偏差。只有等RecyclerView完全布局完成后,获取的宽度才是真实可用的。
内容的提问来源于stack exchange,提问作者kinza
相关产品推荐
相关产品推荐

