【问题标题】:Simple way to do dynamic but square layout进行动态但方形布局的简单方法
【发布时间】:2012-01-24 01:31:45
【问题描述】:

我使用GridView 来显示一堆基本上是LinearLayouts 的视图。我希望LinearLayouts 都是方形的,但我也希望它们是动态大小的——也就是说,有两列,我希望LinearLayouts 根据屏幕的大小拉伸但保持方形。有没有办法通过xml 布局做到这一点,还是我必须以编程方式设置高度和宽度?

【问题讨论】:

    标签: android


    【解决方案1】:

    方形GridView 项目的一个简洁解决方案是扩展RelativeLayout 或LinearLayout 并覆盖onMeasure,如下所示:

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

    【讨论】:

    • 这是使用度量规范而不是使用 setMeasuredDimension() ,所以这很好。但它不是很灵活,因为它总是占用宽度。
    • 正如@DavidMerriman 上面所说,如果视图比它高,这将导致视图延伸到查看区域下方,切断部分视图。
    • 如果将 Math.min(widthMeasureSpec, heightMeasureSpec) 的值传递给 super.onMeasure() 的两个参数会更灵活
    • 这可以轻松用于活动,但不适用于应用小部件。
    【解决方案2】:

    随着 Android Studio 2.3 中引入的新 ConstraintLayout,现在构建响应式布局变得非常容易。

    在父 ConstraintLayout 中,要使其任何子视图/布局动态成正方形,请添加此属性

    app:layout_constraintDimensionRatio="w,1:1"
    

    w 是指定宽度方向的约束,1:1 的比例确保方形布局。

    【讨论】:

    • 确保在子布局中为布局宽度和高度设置“0dp”。否则将无法正常工作。
    • 它也适用于视图本身,我的意思是如果您希望 contrantLayout 的孩子是方形的,您可以直接在子视图中定义 layout_constraintDimensionRatio 属性
    • 非常感谢您的解决方案;如果它使用您的解决方案有助于我的代码的完整要点,我已经做了:gist.github.com/nadar71/006699e31ef7451813e6c97dacfbc5e2
    【解决方案3】:

    我是这样做的:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
        int size;
        if(widthMode == MeasureSpec.EXACTLY && widthSize > 0){
            size = widthSize;
        }
        else if(heightMode == MeasureSpec.EXACTLY && heightSize > 0){
            size = heightSize;
        }
        else{
            size = widthSize < heightSize ? widthSize : heightSize;
        }
    
        int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
        super.onMeasure(finalMeasureSpec, finalMeasureSpec);
    }
    

    使用此实现,您的布局将是方形的,假设宽度和高度之间的尺寸较小。它甚至可以使用动态值进行设置,例如在 LinearLayout 中使用权重。

    【讨论】:

    • 方法“onMeasure”将在扩展 GridView 的类中被覆盖?
    • Tt 在你希望它是方形的类中被覆盖。对于问题的情况,它是 GridView 内的 LinearLayouts。
    • 这个帮助我解决了在 StaggeredGridLayout 中使用方形视图时的一些奇怪问题。我更天真的实现还不够好。谢谢!
    【解决方案4】:

    xml 中没有任何内容可以让您链接宽度和高度属性。可能最简单的做法是继承LinearLayout 并覆盖onMeasure

    @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int size = width > height ? height : width;
        setMeasuredDimension(size, size);
    }
    

    我用它来创建以前总是方形的视图。它仍然适用于LinearLayout。

    有助于执行此操作的更多信息: http://developer.android.com/guide/topics/ui/custom-components.html http://developer.android.com/reference/android/view/View.MeasureSpec.html

    【讨论】:

    • 如果这对你有用,你能把它标记为正确答案吗?
    • 好吧,我找到了一个解决方案,虽然类似,但与此有点不同,我无法使用 onMeasure 让它工作,所以我不确定这段代码 sn-p 是否会为我工作。
    • @Catherine 嘿,Catherine,你能发布你的 sn-p 吗?也许它对其他人有用:-)
    • 别忘了加:super.onMeasure(widthMeasureSpec, widthMeasureSpec);!
    • @deKajoo 使用super.onMeasure(widthMeasureSpec, widthMeasureSpec); 确实会给你一个正方形,但它总是宽度 x 宽度。如果给出的测量值比它们的高宽,你就会遇到问题。我的解决方案使用两个测量值中较小的一个,因此您始终可以获得适合给定矩形的最大正方形。
    【解决方案5】:

    我们可以通过一种非常简单的方式来做到这一点 - 只需调用 super.onMeasure() 两次。

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int squareLen = Math.min(width, height);
    
        super.onMeasure(
            MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY), 
            MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY));
    }
    

    通过两次调用super.onMeasure(),这在绘图过程中效率较低,但它是解决其他答案可能导致的布局问题的简单方法。

    【讨论】:

    • 而不是调用super.onMeasure两次,第二次只需要调用setMeasuredDimension(squareLen, squareLen);
    • 调用super.onMeasure() 两次可确保在给定新视图大小的情况下正确完成布局。如果没有这个,我们需要以不同的方式触发布局或处理不正确的布局。
    【解决方案6】:

    就这么简单:

    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);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        {
            if (widthMeasureSpec < heightMeasureSpec) 
                super.onMeasure(widthMeasureSpec, widthMeasureSpec);
            else
                super.onMeasure(heightMeasureSpec, heightMeasureSpec);
        }
    }
    

    【讨论】:

      【解决方案7】:

      在 XML 中添加以下行:

      app:layout_constraintDimensionRatio="1:1"
      

      【讨论】:

      • 我想发布相同的答案,因为它比 app:layout_constraintDimensionRatio="w,1:1" 更好,因为如果您更改配置,它始终保持比率
      【解决方案8】:

      这是一个适用于所有可以设置为视图或视图组的布局参数的解决方案:

          int width = MeasureSpec.getSize(widthMeasureSpec);
          int height = MeasureSpec.getSize(heightMeasureSpec);
          int widthDesc = MeasureSpec.getMode(widthMeasureSpec);
          int heightDesc = MeasureSpec.getMode(heightMeasureSpec);
          int size = 0;
          if (widthDesc == MeasureSpec.UNSPECIFIED
                  && heightDesc == MeasureSpec.UNSPECIFIED) {
              size = DP(defaultSize); // Use your own default size, in our case
                                      // it's 125dp
          } else if ((widthDesc == MeasureSpec.UNSPECIFIED || heightDesc == MeasureSpec.UNSPECIFIED)
                  && !(widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED)) {
                          //Only one of the dimensions has been specified so we choose the dimension that has a value (in the case of unspecified, the value assigned is 0)
              size = width > height ? width : height;
          } else {
                          //In all other cases both dimensions have been specified so we choose the smaller of the two
              size = width > height ? height : width;
          }
          setMeasuredDimension(size, size);
      

      干杯

      【讨论】:

      • 这个答案有一些有趣的特征并且可能更健壮,但是需要工作并且应该构建传递给 super.onMeasure() 而不是 setMeasuredDimension() 的度量规范,因为它允许各种超级类。
      【解决方案9】:

      我的建议是创建一个继承自 FrameLayout 的自定义布局类。覆盖 OnMeasure() 方法并将您想要的任何控件放在 SquareFrameLayout 中。

      这就是它在 Xamarin.Android 中的完成方式:

      public class SquareFrameLayout : FrameLayout
      {
          private const string _tag = "SquareFrameLayout";
      
          public SquareFrameLayout(Android.Content.Context context):base(context) {}
          public SquareFrameLayout(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer):base(javaReference, transfer) {}
          public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs):base(context, attrs) {}
          public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr):base(context, attrs, defStyleAttr) {}
          public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes):base(context, attrs, defStyleAttr, defStyleRes) {}
      
          protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
          {
              var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
              int widthSize = MeasureSpec.GetSize(widthMeasureSpec);
              var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
              int heightSize = MeasureSpec.GetSize(heightMeasureSpec);
      
              int width, height;
      
              switch (widthMode)
              {
                  case MeasureSpecMode.Exactly:
                      width = widthSize;
                      break;
                  case MeasureSpecMode.AtMost:
                      width = Math.Min(widthSize, heightSize);
                      break;
                  default:
                      width = 100;
                      break;
              }
      
              switch (heightMode)
              {
                  case MeasureSpecMode.Exactly:
                      height = heightSize;
                      break;
                  case MeasureSpecMode.AtMost:
                      height = Math.Min(widthSize, heightSize);
                      break;
                  default:
                      height = 100;
                      break;
              }
      
              Log.Debug(_tag, $"OnMeasure({widthMeasureSpec}, {heightMeasureSpec}) => Width mode: {widthMode}, Width: {widthSize}/{width}, Height mode: {heightMode}, Height: {heightSize}/{height}");
              var size = Math.Min(width, height);
              var newMeasureSpec = MeasureSpec.MakeMeasureSpec(size, MeasureSpecMode.Exactly);
              base.OnMeasure(newMeasureSpec, newMeasureSpec);
          }
      }
      

      如果您希望视图(或任何其他控件)为方形(并居中),只需按以下方式将其添加到您的布局中:

      <your.namespace.SquareFrameLayout
          android:id="@+id/squareContainer"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_gravity="center">
          <View
              android:id="@+id/squareContent"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />
      </your.namespace.SquareFrameLayout>
      

      【讨论】:

        【解决方案10】:

        查看SquareLayout,这是一个Android 库,它为不同的布局提供了一个包装类,在不丢失任何核心功能的情况下将它们呈现为平方尺寸。

        尺寸是在布局渲染之前计算的,因此一旦获得视图,就没有重新渲染或任何类似的调整。

        要使用库,请将其添加到您的 build.gradle:

        repositories {
            maven {
                url "https://maven.google.com"
            }
        }
        
        dependencies {
            compile 'com.github.kaushikthedeveloper:squarelayout:0.0.3'
        }
        

        您需要的是SquareLinearLayout。

        【讨论】:

        • 在真实设备上不适合我 :( 在 Android Studio 预览版上渲染良好,但在设备上不工作
        【解决方案11】:

        对于任何想要使用 Kotlin 解决方案的人,这就是我使用 FrameLayout 所做的。

        package your.package.name
        
        import android.content.Context
        import android.util.AttributeSet
        import android.widget.FrameLayout
        
        class SquareLayout: FrameLayout {
        
            constructor(ctx: Context) : super(ctx)
            constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
        
            override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
                if (widthMeasureSpec < heightMeasureSpec)
                    super.onMeasure(widthMeasureSpec, widthMeasureSpec)
                else
                    super.onMeasure(heightMeasureSpec, heightMeasureSpec)
            }
        }
        

        【讨论】:

          【解决方案12】:

          试试这个代码:

          public class SquareRelativeLayout extends RelativeLayout {
              public SquareRelativeLayout(Context context) {
                  super(context);
              }
          
              public SquareRelativeLayout(Context context, AttributeSet attrs) {
                  super(context, attrs);
              }
          
              @Override
              protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
                  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
                  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
                  int heightSize = MeasureSpec.getSize(heightMeasureSpec);
          
                  int size;
                  if (widthMode == MeasureSpec.EXACTLY && widthSize > 0) {
                      size = widthSize;
                  } else if (heightMode == MeasureSpec.EXACTLY && heightSize > 0) {
                      size = heightSize;
                  } else {
                      size = widthSize < heightSize ? widthSize : heightSize;
                  }
          
                  int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
                  super.onMeasure(finalMeasureSpec, finalMeasureSpec);
              }
          }
          

          【讨论】:

            【解决方案13】:
                                      <androidx.constraintlayout.widget.ConstraintLayout
                                        xmlns:app="http://schemas.android.com/apk/res-auto"
                                        xmlns:tools="http://schemas.android.com/tools" 
                                        android:layout_width="match_parent"
                                        android:layout_height="match_parent" >
            
                                        <ImageView
                                            android:id="@+id/imageView"
                                            android:layout_width="match_parent"
                                            android:layout_height="0dp"
                                            android:layout_gravity="center_horizontal"
                                            android:scaleType="fitCenter"
                                            app:layout_constraintBottom_toBottomOf="parent"
                                            app:layout_constraintDimensionRatio="w,1:1"
                                            app:layout_constraintEnd_toEndOf="parent"
                                            app:layout_constraintStart_toStartOf="parent"
                                            app:layout_constraintTop_toTopOf="parent"
                                            tools:src="@tools:sample/avatars" />
            
            
                                    </androidx.constraintlayout.widget.ConstraintLayout>
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-07-17
              • 2015-05-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-10-27
              相关资源
              最近更新 更多