如何在安卓中为布局设置固定的宽高比?

49

我想将布局的宽度设置为“fill_parent”,而视图的高度保持相同的长度,使布局成为一个正方形。

欢迎提出任何建议。


使用 wrap_content。它将采用视图的相同宽度和长度。 - Chirag
1
可能是填充剩余空间的固定宽高比SurfaceView的重复问题。 - Tim
我自己也曾经为此苦苦挣扎,最终我决定要重写我想要限制的视图的onMeasure方法,并通过编程方式设置其尺寸。请参见链接的重复内容。 - Tim
1
是的,我已经为你的答案投了赞,而且像newbyca提供的那样做起来更容易 :D - Bolton
这里的答案难道不更接近你所寻找的吗?:https://dev59.com/fmw05IYBdhLWcg3wv0R6 - ThomasW
9个回答

113

引入ConstraintLayout后,您无需编写任何代码或使用第三方库或依赖于已在26.0.0版本中弃用的PercentFrameLayout

下面是使用ConstraintLayout保持1:1布局纵横比的示例:

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginEnd="0dp"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp"
        android:background="@android:color/black"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </FrameLayout>

</android.support.constraint.ConstraintLayout>

或者,您可以直接在布局编辑器中编辑布局,而无需编辑 XML 文件:

布局编辑器


你好 @Eugene ,每当我使用这个来捕捉视频时,宽度总是看起来有点拉伸,可能是什么原因引起的呢? - Israel Meshileya
请分享截图。 - Eugene Brusov
https://meshileya.github.io/error.png 这是@Eugene的屏幕截图。感谢您的回复。 - Israel Meshileya
1
尝试访问该页面时出现404错误。 - Eugene Brusov
我不知道你是否能够看到它。 - Israel Meshileya
显示剩余2条评论

22

在您的build.gradle文件中添加:

compile 'com.android.support:percent:23.1.1'

在你的layout.xml中,一个PercentFrameLayout包裹任何想要按比例显示的视图或视图组,其中:

android:layout_width="match_parent"
android:layout_height="wrap_content"

接下来,如果您想让视图或视图组遵循一个比例,请替换android:layout_widthandroid:layout_height

    app:layout_aspectRatio="178%"
    app:layout_widthPercent="100%"

如果您有一个视图或视图组,其宽高比为16:9(1.78:1),并且宽度与父级相匹配,高度会相应调整。

注意:重要的是您删除正常的宽度和高度属性。 Lint会抱怨,但它会起作用。


1
我同意这里说的一切,除了你提到的删除普通的宽度和高度属性是重要的。我尝试过了,但对我没有用。相反,我必须将它们设置为以下内容:android:layout_width="0dp" android:layout_height="0dp",如此处所述:https://dev59.com/tZPfa4cB1Zd3GeqPA0TJ - Nick.D
警告:仅当容器宽度> =容器高度* 16/9时,此功能才有效。例如,如果您的容器宽度为1000dp,其高度为50dp,则子元素将适合宽度并具有高度1000 * 9/16 = 562.5。 - Tim Autin
2
PercentRelativeLayoutPercentFrameLayout现已被弃用,建议改用ConstraintLayout - advice

18

你可以尝试将layout_height最初设置为wrap_content。但从那里开始,我认为你需要进入代码。只是为了尝试,在你的活动中尝试这样做:

@Override
public void onResume(){
    super.onResume();
    findViewById(R.id.squareView).getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override public void onGlobalLayout() {
            View squareView = findViewById(R.id.squareView);
            LayoutParams layout = squareView.getLayoutParams();
            layout.height = squareView.getWidth();
            squareView.setLayoutParams(layout);
            squareView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    });
}

这里的R.id.squareView是指要操作的视图的ID。onGlobalLayout用来确保squareView.getWidth()有意义的值。


removeGlobalOnLayoutListener is deprecated. use removeOnGlobalLayoutListener(...) - Aks4125

6
我为类似用例创建了一个布局库,可以自由使用。 RatioLayouts

安装

将此添加到文件顶部。

repositories {
    maven {
        url  "http://dl.bintray.com/riteshakya037/maven" 
    }
}


dependencies {
    compile 'com.ritesh:ratiolayout:1.0.0'
}

使用方法

在您的布局中的根视图上定义“app”命名空间

xmlns:app="http://schemas.android.com/apk/res-auto"

在您的布局中引入这个库。
<com.ritesh.ratiolayout.RatioRelativeLayout
        android:id="@+id/activity_main_ratio_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:fixed_attribute="WIDTH" // Fix one side of the layout 
        app:horizontal_ratio="2" // ratio of 2:3
        app:vertical_ratio="3">

现在它已经被弃用了。 - famfamfam

1
如果您想保留宽高比并且父布局必须是“wrap_content”(对于具有可变文本的文本视图非常有用),则可以添加一个虚拟视图,该视图保持宽高比,并且其他元素必须被约束到此视图上:
    <androidx.constraintlayout.widget.ConstraintLayout     
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    <View
        android:id="@+id/ratio_constraint"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="122:246"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="@id/ratio_constraint"
        app:layout_constraintStart_toStartOf="@id/ratio_constraint"
        app:layout_constraintTop_toTopOf="@id/ratio_constraint"
        tools:text="The TextView which can extend the layout" />

  </androidx.constraintlayout.widget.ConstraintLayout>

enter image description here


1
在我的开发场景中,我需要为一个父级为LinearLayout而不是ConstraintLayout的CardView设置固定的宽高比。然后我找到了一种通用的解决方案,可以让Android的View保持固定的宽高比。
在onMeasure()方法中,我们可以根据所需的宽高比和布局系统提供的可用宽度和高度来计算所需的宽度和高度。以下是一个示例:
class FixedAspectRatioView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val height = (width / 1.618).roundToInt()
        val newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
        super.onMeasure(widthMeasureSpec, newHeightMeasureSpec)
    }
}

上述代码用于保持视图的固定长宽比,大约为1.618。以下是代码的详细解析:
  1. val width = MeasureSpec.getSize(widthMeasureSpec): 这行代码使用MeasureSpec.getSize()widthMeasureSpec中获取可用的宽度。这个宽度将用于根据所需的宽高比计算相应的高度。
  2. val height = (width / 1.618).roundToInt(): 这行代码通过将可用宽度(width)除以所需的宽高比(1.618)来计算所需的高度。使用roundToInt()将结果四舍五入为最接近的整数。
  3. val newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY): 这行代码使用MeasureSpec.makeMeasureSpec()创建一个新的高度测量规范(newHeightMeasureSpec)。高度设置为计算得到的height,模式设置为MeasureSpec.EXACTLY,表示高度应该完全按照指定的值。
  4. super.onMeasure(widthMeasureSpec, newHeightMeasureSpec): 这行代码使用原始的widthMeasureSpec和新的newHeightMeasureSpec调用父类的onMeasure()方法。这确保视图使用所需的宽度和高度进行测量,保持固定的宽高比。
总的来说,这段代码片段应该能够正确地保持视图的固定长宽比,大约为1.618。

0
 final RelativeLayout rlMyList = findViewById(R.id.rlMyList);
 rlMyList.postDelayed(new Runnable() {
     @Override
     public void run() {
          int width = rlMyList.getWidth();
          LayoutParams lp = (LayoutParams) rlMyList.getLayoutParams();
          lp.width = width;
          lp.height = (int) (width * 0.67);// in my case ratio 3:2
          rlMyList.setLayoutParams(lp);
     }
 },500);

0
LinearLayout ll = (LinearLayout)findViewById(R.id.LL);


int width = ll.getWidth();
LinearLayout.LayoutParams ll_params = new LinearLayout.LayoutParams(width, width);
ll.setLayoutParams(ll_params);

1
这个大致上还可以,但它完全取决于何时被调用。例如,如果您将此代码放在onCreate中,ll.getWidth()将返回0。 - newbyca

-1

将高度设置为fill_parent,宽度设置为wrap_content

并使用android:adjustViewBounds="true"来固定宽高比。


1
AdjustViewBounds 只适用于 ImageView,它限制的是 drawable 的比例,而不是视图的比例。 - Tim
你想让布局变成正方形吗? - Aditya Nikhade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接