回收视图的 GridLayoutManager 上的方块布局

我试着用正方形图像做一个网格布局。我认为通过操纵 onMeasure来操纵 GridLayoutManager一定是可能的

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

而不是

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

但不幸的是,这招不管用。

有什么想法吗?

45062 次浏览

为了在我的回收视图中包含方形元素,我为根视图元素提供了一个简单的包装器; 我使用以下 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 布局中,我刚刚引用了自定义视图,如下所示。不过,您也可以通过编程方式完成此操作。

<?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);
}

请尝试这个 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);
}
}

以及 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>

约束布局解决了这个问题。使用 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="0dp"
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>

剪辑

将 ImageView 宽度设置为 0dp.match_parent现在不推荐用于 ConstraintLayout。

我再次推荐相对较新的“百分比”布局:

<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 快得多,尽管有一天我们可能不再关心它了。

我不喜欢所选择的答案,所以让我提供我的: 而不是包装整个项目布局在 SomDammyLayoutWithFixedAspectratio 你可以黑客 GridLayoutManager 和重写内部的措施孩子代码。我替换了这些台词:

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;
}

看起来没问题。

不要误解我的意思,这也是相当混乱的,但至少这个解决方案不会因为扩展视图层次结构而损害应用程序的性能

从 API 26(Support Library 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"是这里的关键属性

我有类似的问题,我不得不膨胀的观点,这将是正方形的网格的回收者的观点。下面是我做这件事的方法。

在 onCreateViewHolder 方法中,我使用 ViewTreeObserver 和 GlobalLayoutListener 来获得布局的测量宽度。布局在 width 属性中具有 match _ father 值。任何我的回收视图有布局在中心水平。

final View view = LayoutInflater.from(mActivity).inflate(R.layout.list_item_deals, parent, false);
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int side = view.getMeasuredWidth();


ViewGroup.LayoutParams lp = view.getLayoutParams();
lp.width = side;
lp.height = side;
view.setLayoutParams(lp);
}
});

参考图像

Androidx 的 ConstraintLayout 的一个小更新。

把这一行加入到你的 build.gradle 中:

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'

我想得到一个回收视图与 GridLayoutManager 与正方形卡片视图,我使用这样的项目布局:

<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
>


<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="0dp"
android:layout_height="0dp"
card_view:cardElevation="4dp"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
>

关于约束布局

  • Lay_ width = “ Match _ father”对于让项目填充回收视图所提供的空间非常重要
  • Lay_ height = “ Wrap _ content”不允许项填充回收视图给出的所有高度,而是使用 ConstraintLayout 提供的约束高度。在我的例子中,当我使用 FrameLayout 或 LinearLayout 时,条目是“高的”。

在子节点上,在我的例子中是 CardView

  • 将大小限制为零是很重要的: lay _ width = “0dp”和 outs _ height = “0dp”这意味着宽度和高度受到限制
  • LaysconstraintVersionratio = “ H,1:1”可以产生预期的效果,通过设置 H,您可以定义高度约束为1:1的比例。

请参阅关于 离开现场的一些详细说明。