用于RecyclerView的GridLayoutManager上的方形布局

我尝试使用方形图像进行网格布局。 我认为,通过操纵onMeasure来操作GridLayoutManager一定是可能的

 super.onMeasure(recycler, state, widthSpec, widthSpec); 

代替

 super.onMeasure(recycler, state, widthSpec, heightSpec); 

但不幸的是,这并没有奏效。

有任何想法吗?

为了让我的RecyclerView中的方形元素,我提供了一个简单的包装我的根视图元素; 我使用下面的SquareRelativeLayout来代替RelativeLayout

 package net.simplyadvanced.widget; import android.content.Context; import android.util.AttributeSet; import android.widget.RelativeLayout; /** A RelativeLayout that will always be square -- same width and height, * where the height is based off the width. */ public class SquareRelativeLayout extends RelativeLayout { public SquareRelativeLayout(Context context) { super(context); } public SquareRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(VERSION_CODES.LOLLIPOP) public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Set a square layout. super.onMeasure(widthMeasureSpec, widthMeasureSpec); } } 

然后,在适配器的XML布局中,我刚刚引用了自定义视图,如下所示。 尽pipe如此,你也可以通过编程来完成这个任务

 <?xml version="1.0" encoding="utf-8"?> <net.simplyadvanced.widget.SquareRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/elementRootView" android:layout_width="wrap_content" android:layout_height="wrap_content"> <!-- More widgets here. --> </net.simplyadvanced.widget.SquareRelativeLayout> 

注意:根据网格的方向,您可能希望基于高度( GridLayoutManager.HORIZONTAL )而不是基于宽度( GridLayoutManager.VERTICAL )的高度。

如果有人想以不同的方式缩放视图 – 这是你如何做到这一点:

 private static final double WIDTH_RATIO = 3; private static final double HEIGHT_RATIO = 4; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = (int) (HEIGHT_RATIO / WIDTH_RATIO * widthSize); int newHeightSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, newHeightSpec); } 

约束布局解决了这个问题。 使用app:layout_constraintDimensionRatio="H,1:1"

recyclerview_grid_layout.xml

 <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <ImageView android:id="@+id/imageview" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintDimensionRatio="H,1:1" android:scaleType="centerCrop" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> </android.support.constraint.ConstraintLayout> 

请试试这个FrameLayout的扩展。 它执行双重测量来提高一致性。 它还支持自定义的XML属性来设置布局所需的纵横比

 public class StableAspectFrameLayout extends FrameLayout { private int aspectWidth = 1; private int aspectHeight = 1; public StableAspectFrameLayout(Context context) { this(context, null, 0); } public StableAspectFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public StableAspectFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); extractCustomAttrs(context, attrs); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public StableAspectFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); extractCustomAttrs(context, attrs); } private void extractCustomAttrs(Context context, AttributeSet attrs) { if (attrs == null) return; TypedArray a = context.getResources().obtainAttributes(attrs, R.styleable.StableAspectFrameLayout); try { aspectWidth = a.getInteger(R.styleable.StableAspectFrameLayout_aspect_width, 1); aspectHeight = a.getInteger(R.styleable.StableAspectFrameLayout_aspect_height, 1); } finally { a.recycle(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int newSpecWidth = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY); int newH = Math.round(((float) getMeasuredWidth()) * aspectHeight / aspectWidth); int newSpecHeigh = MeasureSpec.makeMeasureSpec(newH, MeasureSpec.EXACTLY); super.onMeasure(newSpecWidth, newSpecHeigh); } } 

以及attrs.xml的内容

 <?xml version="1.0" encoding="utf-8"?> <resources> <!-- StableAspectFrameLayout --> <declare-styleable name="StableAspectFrameLayout"> <attr name="aspect_width" format="integer"/> <attr name="aspect_height" format="integer"/> </declare-styleable> </resources> 

我再次推荐最近的“百分比”布局。 使用依赖'com.android.support:percent:25.2.0' ,你可以做这样的事情:

 <android.support.percent.PercentFrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/image" app:layout_widthPercent="100%" app:layout_aspectRatio="100%" android:padding="10dp" android:scaleType="centerCrop" android:cropToPadding="true" tools:background="#efdbed" /> </android.support.percent.PercentFrameLayout> 

这可能比ConstraintLayout快得多,但有一天我们可能不会在意了。

启动API 26(支持库26.0),可以使用公开宽高比属性强制视图平方的ConstraintLayout: https : //developer.android.com/training/constraint-layout/index.htm

 android { compileSdkVersion 26 buildToolsVersion '26.0.2' ... } ... dependencies { compile 'com.android.support:appcompat-v7:26.0.2' compile 'com.android.support.constraint:constraint-layout:1.1.0-beta1' //use whatever version is current } 

我在GridLayoutManager中使用的布局示例:

 <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.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="match_parent" android:layout_margin="@dimen/margin_small" android:background="@drawable/border_gray" android:gravity="center"> <android.support.constraint.ConstraintLayout android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintDimensionRatio="h,1:1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <!-- place your content here --> </android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout> 

app:layout_constraintDimensionRatio="h,1:1"是这里的关键属性

我不喜欢select的答案,所以让我提供我的:而不是包装在SomeDammyLayoutWithFixedAspectRatio整个项目布局,你可以砍掉GridLayoutManager,并重写measureChild内的代码。 我已经replace了这些行:

 if (mOrientation == VERTICAL) { wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, horizontalInsets, lp.width, false); hSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getHeightMode(), verticalInsets, lp.height, true); } else { hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, verticalInsets, lp.height, false); wSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getWidthMode(), horizontalInsets, lp.width, true); } 

至:

 if (mOrientation == VERTICAL) { wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, horizontalInsets, lp.width, false); hSpec = wSpec; } else { hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, verticalInsets, lp.height, false); wSpec = hSpec; } 

它似乎工作正常。

不要误解我的意思,这也太乱了,但至less这个解决scheme不会通过扩展视图层次来伤害应用的性能