RecyclerView子项宽高相等实现遇阻:获取CardView宽度返回-1
解决RecyclerView子项宽高相等(获取宽度返回-1)的问题
我之前也碰到过完全一样的坑!在onBindViewHolder里直接拿View的宽高肯定不行——这时候视图还没完成测量和布局流程,所以返回的是-1或者默认占位值。下面给你几个靠谱的解决办法,按实现复杂度和推荐度排序:
1. 最省心的方案:用ConstraintLayout直接设置宽高比
不用写一行Java代码,直接在子项布局(item.xml)里用ConstraintLayout的比例属性实现正方形,这是最推荐的方式:
<!-- item.xml 核心代码 --> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.cardview.widget.CardView android:id="@+id/card_view" android:layout_width="match_parent" android:layout_height="0dp" <!-- 高度设为0,让比例属性控制 --> app:layout_constraintDimensionRatio="1:1" <!-- 宽高1:1,实现正方形 --> app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"> <!-- 你的子项内容,比如TextView、ImageView等 --> </androidx.cardview.widget.CardView> </androidx.constraintlayout.widget.ConstraintLayout>
2. 代码层面解决:监听视图布局完成后再设置高度
如果必须用Java动态控制,就得等视图完成测量后再获取宽高。在Adapter的onBindViewHolder里给CardView添加布局监听:
@Override public void onBindViewHolder(ViewHolder holder, int position) { // 先完成其他数据绑定逻辑 final CardView cardView = holder.cardView; // 监听视图布局完成事件 cardView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // 必须移除监听,避免重复触发影响性能 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { cardView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { cardView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } // 获取实际测量宽度,设置为高度 int realWidth = cardView.getMeasuredWidth(); ViewGroup.LayoutParams params = cardView.getLayoutParams(); params.height = realWidth; cardView.setLayoutParams(params); } }); }
3. 全局控制:自定义LayoutManager
如果你的整个RecyclerView所有子项都需要是正方形,可以自定义LayoutManager来统一处理,适合大规模复用的场景:
public class SquareLinearLayoutManager extends LinearLayoutManager { public SquareLinearLayoutManager(Context context) { super(context); } public SquareLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } @Override public void layoutDecorated(View child, int left, int top, int right, int bottom) { // 把高度强制设置为宽度(right-left是子项的实际宽度) super.layoutDecorated(child, left, top, right, top + (right - left)); } }
然后在Activity里给RecyclerView设置这个LayoutManager:
recyclerView.setLayoutManager(new SquareLinearLayoutManager(this));
为什么你原来的方法会返回-1?
onBindViewHolder是在绑定数据到视图的阶段调用,这时候视图还没被添加到RecyclerView的布局树中,也没有完成测量流程,所以getWidth()/getMeasuredWidth()返回的是未测量的默认值-1,必须等视图完成布局后才能拿到真实的宽高。
内容的提问来源于stack exchange,提问作者Bishoy Kamel
相关产品推荐
相关产品推荐

