【问题标题】:How to size an Android view based on its parent's dimensions如何根据父级的尺寸调整 Android 视图的大小
【发布时间】:2010-01-29 01:30:46
【问题描述】:

如何根据父布局的大小调整视图的大小。例如,我有一个填满整个屏幕的RelativeLayout,我想要一个子视图,比如ImageView,占据整个高度和宽度的 1/2?

我已尝试覆盖所有 onMeasureonLayoutonSizeChanged 等,但无法正常工作....

【问题讨论】:

标签: android layout


【解决方案1】:

我不知道是否有人仍在阅读此主题,但 Jeff 的解决方案只会让您走到一半(有点字面意思)。他的 onMeasure 将做的是在一半的父母中显示一半的图像。问题是在setMeasuredDimension 之前调用 super.onMeasure 将根据原始大小测量视图中的所有子项,然后在setMeasuredDimension 调整其大小时将视图切成两半。

相反,您需要调用 setMeasuredDimension(根据 onMeasure 覆盖的要求)为您的视图提供新的 LayoutParams然后调用 super.onMeasure .请记住,您的 LayoutParams 派生自您的视图的父类型,而不是您的视图类型。

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
   int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
   int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
   this.setMeasuredDimension(parentWidth/2, parentHeight);
   this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

我相信您唯一会遇到父级 LayoutParams 问题的情况是父级是 AbsoluteLayout(已弃用但有时仍然有用)。

【讨论】:

  • 根据我的经验,这很少奏效,尤其是。当调用 fill/match_parent 或 weights 时。最好在 setMeasuredDimension 之后调用 measureChildren(例如 MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.AT_MOST))。
  • MeasureSpec.getSize(widthMeasureSpec) 真的给出了正确的父母宽度吗?对我来说,它只是返回非常接近实际宽度但不精确的随机宽度。
  • @M.Schenk 有。我在加权布局中有一个 VideoView。我需要宽度是屏幕大小的百分比,并且我需要保持纵横比。这会拉伸视频,所以wrap_content 不会这样做。 Vic 的回答不适用于这种情况,但@M.Schenk 的评论可以。它还节省了额外的布局通道。您还应该将其发布为可见性的答案。
  • super.onMeasure() 的调用不会简单地覆盖和取消之前对this.setMeasuredDimension() 的调用的效果吗?
  • 我也很好奇,正如@Spinner 所提到的,为什么对super.onMeasure() 的调用不会覆盖之前的计算。
【解决方案2】:

您可以通过创建自定义视图并覆盖 onMeasure() 方法来解决此问题。如果您总是在 xml 中对 layout_width 使用“fill_parent”,那么传递给 onMeasusre() 方法的 widthMeasureSpec 参数应该包含父级的宽度。

public class MyCustomView extends TextView {

    public MyCustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
        this.setMeasuredDimension(parentWidth / 2, parentHeight);
    }
}   

您的 XML 看起来像这样:

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <view
        class="com.company.MyCustomView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

【讨论】:

  • @jeff => 为什么是 parentWidth / 2 ??
  • @Leon_SFS:问题要求:“整个高度,宽度的 1/2”
【解决方案3】:

我发现最好不要自己设置测量尺寸。实际上,在父视图和子视图之间进行了一些协商,您不想重写所有 code

不过,您可以做的是修改 measureSpecs,然后用它们调用 super。您的视图永远不会知道它正在从其父级获取修改后的消息,并且会为您处理所有事情:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    int myWidth = (int) (parentHeight * 0.5);
    super.onMeasure(MeasureSpec.makeMeasureSpec(myWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}

【讨论】:

    【解决方案4】:

    在 XML 上定义布局和视图时,可以将视图的布局宽度和高度指定为 wrap_contentfill_parent。占据一半的区域有点困难,但如果你在另一半有你想要的东西,你可以做如下的事情。

    <LinearLayout android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <ImageView android:layout_height="fill_parent"
            android:layout_width="0dp"
            android:layout_weight="1" />
        <ImageView android:layout_height="fill_parent"
            android:layout_width="0dp"
            android:layout_weight="1" />
    </LinearLayout>
    

    赋予两个相同重量的东西意味着它们将拉伸以占据相同比例的屏幕。有关布局的更多信息,请参阅the dev docs.

    【讨论】:

    • 我不推荐这个,但是......作为一个黑客,如果你确实使用上述方法,你可以将其中一个视图设置为“虚拟”并设置 android:visibility="invisible"填满一半的区域
    • 只使用一半区域的目的是什么?你打算在另一半展示什么吗?您对剩余区域的处理将告知最佳方法
    • 这绝对应该是#1 - 如果你可以在你的 xml 中做到这一点,为什么要在你的 java 中做到这一点?
    【解决方案5】:

    我相信Mayras XML-approach 可以很整洁。但是,可以通过设置 weightSum 来使其更准确,只有一个视图。我不会再将其称为 hack,但我认为这是最直接的方法:

    <LinearLayout android:layout_width="fill_parent"
                  android:layout_height="fill_parent"
                  android:weightSum="1">
        <ImageView android:layout_height="fill_parent"
                   android:layout_width="0dp"
                   android:layout_weight="0.5"/>
    </LinearLayout>
    

    像这样,您可以使用任何重量,例如 0.6(和居中)是我喜欢用于按钮的重量。

    【讨论】:

      【解决方案6】:

      试试这个

      int parentWidth = ((parentViewType)childView.getParent()).getWidth();
      int parentHeight = ((parentViewType)childView.getParent()).getHeight();
      

      那么你可以使用 LinearLayout.LayoutParams 来设置 chileView 的参数

      LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(childWidth,childLength);
      childView.setLayoutParams(params);
      

      【讨论】:

        【解决方案7】:

        您现在可以使用 PercentRelativeLayout。繁荣!问题解决了。

        【讨论】:

        【解决方案8】:

        Roman,如果您想在 Java 代码(ViewGroup 后代)中进行布局,这是可能的。诀窍是您必须同时实现 onMeasure 和 onLayout 方法。 onMeasure 首先被调用,您需要在那里“测量”子视图(有效地将其调整为所需的值)。您需要在 onLayout 调用中再次调整它的大小。如果您未能执行此序列或未能在 onMeasure 代码末尾调用 setMeasuredDimension(),您将无法获得结果。为什么会以如此复杂和脆弱的方式设计出来。

        【讨论】:

          【解决方案9】:

          如果另一边是空的。我想最简单的方法是添加一个空视图(例如线性布局),然后将两个视图的宽度设置为 fill_parent 并将它们的权重都设置为 1。

          这适用于 LinearLayout...

          【讨论】:

            【解决方案10】:

            是这样的:

            <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context="com.company.myapp.ActivityOrFragment"
                android:id="@+id/activity_or_fragment">
            
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/linearLayoutFirst"
                android:weightSum="100">   <!-- Overall weights sum of children elements -->
            
                <Spinner
                    android:layout_width="0dp"   <!-- It's 0dp because is determined by android:layout_weight -->
                    android:layout_weight="50"
                    android:layout_height="wrap_content"
                    android:id="@+id/spinner" />
            
            </LinearLayout>
            
            
            <LinearLayout
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:layout_below="@+id/linearLayoutFirst"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:id="@+id/linearLayoutSecond">
            
                <EditText
                    android:layout_height="wrap_content"
                    android:layout_width="0dp"
                    android:layout_weight="75"
                    android:inputType="numberDecimal"
                    android:id="@+id/input" />
            
                <TextView
                    android:layout_height="wrap_content"
                    android:layout_width="0dp"
                    android:layout_weight="25"
                    android:id="@+id/result"/>
            
            </LinearLayout>
            </RelativeLayout>
            

            【讨论】:

              【解决方案11】:

              这个问题我纠结了很久,我想改变我的 DrawerLayout 的抽屉宽度,它是其父级的第二个子级。 最近想通了,不知道有没有更好的办法。

              override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
                  super.onLayout(changed, l, t, r, b)
                  (getChildAt(1).layoutParams as LayoutParams).width = measuredWidth/2
              }
              

              【讨论】:

              • 使用这种方式的问题是在onLayout里面设置布局参数会导致无限递归,UI线程可能永远不会空闲
              • @TusharKathuria 怎么会?这样布局后,子LP的宽度模式是EXACTLY,不会再改变了。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-01-27
              • 2014-12-06
              • 2014-12-29
              • 2015-03-12
              • 1970-01-01
              • 2016-05-29
              • 1970-01-01
              相关资源
              最近更新 更多