【问题标题】:Replacement for the linearLayout weights mechanism替换 linearLayout 权重机制
【发布时间】:2012-09-14 15:47:24
【问题描述】:

背景:

  • 出于性能考虑,Google 建议避免使用嵌套加权线性布局。
  • 使用嵌套加权线性布局很难读取、写入和维护。
  • 仍然没有很好的选择来放置可用大小的 % 的视图。唯一的解决方案是权重和使用 OpenGL。甚至没有像 WPF/Silverlight 上显示的“viewBox”之类的东西来自动缩放。

这就是为什么我决定创建自己的布局,您可以告诉每个孩子的确切权重(和周围的权重)与其大小相比应该是什么。

看来我已经成功了,但由于某种原因,我认为有些错误我无法追踪。

其中一个错误是 textView,即使我为它提供了很多空间,它也会将文本放在顶部而不是中心。另一方面,imageViews 工作得很好。另一个错误是,如果我在自定义布局中使用布局(例如 frameLayout),则不会显示其中的视图(但布局本身会)。

请帮我弄清楚它发生的原因。

如何使用:代替线性布局的下一个用法(我故意使用长 XML,以展示我的解决方案如何缩短内容):

<LinearLayout 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" android:orientation="vertical">

  <View android:layout_width="wrap_content" android:layout_height="0px"
    android:layout_weight="1" />

  <LinearLayout android:layout_width="match_parent"
    android:layout_height="0px" android:layout_weight="1"
    android:orientation="horizontal">

    <View android:layout_width="0px" android:layout_height="wrap_content"
      android:layout_weight="1" />

    <TextView android:layout_width="0px" android:layout_weight="1"
      android:layout_height="match_parent" android:text="@string/hello_world"
      android:background="#ffff0000" android:gravity="center"
      android:textSize="20dp" android:textColor="#ff000000" />

    <View android:layout_width="0px" android:layout_height="wrap_content"
      android:layout_weight="1" />

  </LinearLayout>
  <View android:layout_width="wrap_content" android:layout_height="0px"
    android:layout_weight="1" />
</LinearLayout>

我所做的很简单(x 是将视图本身放在权重列表中的位置):

<com.example.weightedlayouttest.WeightedLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res/com.example.weightedlayouttest"
  xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
  android:layout_height="match_parent" tools:context=".MainActivity">

  <TextView android:layout_width="0px" android:layout_height="0px"
    app:horizontalWeights="1,1x,1" app:verticalWeights="1,1x,1"
    android:text="@string/hello_world" android:background="#ffff0000"
    android:gravity="center" android:textSize="20dp" android:textColor="#ff000000" />

</com.example.weightedlayouttest.WeightedLayout>

我的特殊布局代码是:

public class WeightedLayout extends ViewGroup
  {
  @Override
  protected WeightedLayout.LayoutParams generateDefaultLayoutParams()
    {
    return new WeightedLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
    }

  @Override
  public WeightedLayout.LayoutParams generateLayoutParams(final AttributeSet attrs)
    {
    return new WeightedLayout.LayoutParams(getContext(),attrs);
    }

  @Override
  protected ViewGroup.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p)
    {
    return new WeightedLayout.LayoutParams(p.width,p.height);
    }

  @Override
  protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p)
    {
    final boolean isCorrectInstance=p instanceof WeightedLayout.LayoutParams;
    return isCorrectInstance;
    }

  public WeightedLayout(final Context context)
    {
    super(context);
    }

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

  public WeightedLayout(final Context context,final AttributeSet attrs,final int defStyle)
    {
    super(context,attrs,defStyle);
    }

  @Override
  protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
    {
    for(int i=0;i<this.getChildCount();++i)
      {
      final View v=getChildAt(i);
      final WeightedLayout.LayoutParams layoutParams=(WeightedLayout.LayoutParams)v.getLayoutParams();
      //
      final int availableWidth=r-l;
      final int totalHorizontalWeights=layoutParams.getLeftHorizontalWeight()+layoutParams.getViewHorizontalWeight()+layoutParams.getRightHorizontalWeight();
      final int left=l+layoutParams.getLeftHorizontalWeight()*availableWidth/totalHorizontalWeights;
      final int right=r-layoutParams.getRightHorizontalWeight()*availableWidth/totalHorizontalWeights;
      //
      final int availableHeight=b-t;
      final int totalVerticalWeights=layoutParams.getTopVerticalWeight()+layoutParams.getViewVerticalWeight()+layoutParams.getBottomVerticalWeight();
      final int top=t+layoutParams.getTopVerticalWeight()*availableHeight/totalVerticalWeights;
      final int bottom=b-layoutParams.getBottomVerticalWeight()*availableHeight/totalVerticalWeights;
      //
      v.layout(left+getPaddingLeft(),top+getPaddingTop(),right+getPaddingRight(),bottom+getPaddingBottom());
      }
    }

  // ///////////////
  // LayoutParams //
  // ///////////////
  public static class LayoutParams extends ViewGroup.LayoutParams
    {
    int _leftHorizontalWeight =0,_rightHorizontalWeight=0,_viewHorizontalWeight=0;
    int _topVerticalWeight    =0,_bottomVerticalWeight=0,_viewVerticalWeight=0;

    public LayoutParams(final Context context,final AttributeSet attrs)
      {
      super(context,attrs);
      final TypedArray arr=context.obtainStyledAttributes(attrs,R.styleable.WeightedLayout_LayoutParams);
        {
        final String horizontalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_horizontalWeights);
        //
        // handle horizontal weight:
        //
        final String[] words=horizontalWeights.split(",");
        boolean foundViewHorizontalWeight=false;
        int weight;
        for(final String word : words)
          {
          final int viewWeightIndex=word.lastIndexOf('x');
          if(viewWeightIndex>=0)
            {
            if(foundViewHorizontalWeight)
              throw new IllegalArgumentException("found more than one weights for the current view");
            weight=Integer.parseInt(word.substring(0,viewWeightIndex));
            setViewHorizontalWeight(weight);
            foundViewHorizontalWeight=true;
            }
          else
            {
            weight=Integer.parseInt(word);
            if(weight<0)
              throw new IllegalArgumentException("found negative weight:"+weight);
            if(foundViewHorizontalWeight)
              _rightHorizontalWeight+=weight;
            else _leftHorizontalWeight+=weight;
            }
          }
        if(!foundViewHorizontalWeight)
          throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value");
        }
        //
        // handle vertical weight:
        //
        {
        final String verticalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_verticalWeights);
        final String[] words=verticalWeights.split(",");
        boolean foundViewVerticalWeight=false;
        int weight;
        for(final String word : words)
          {
          final int viewWeightIndex=word.lastIndexOf('x');
          if(viewWeightIndex>=0)
            {
            if(foundViewVerticalWeight)
              throw new IllegalArgumentException("found more than one weights for the current view");
            weight=Integer.parseInt(word.substring(0,viewWeightIndex));
            setViewVerticalWeight(weight);
            foundViewVerticalWeight=true;
            }
          else
            {
            weight=Integer.parseInt(word);
            if(weight<0)
              throw new IllegalArgumentException("found negative weight:"+weight);
            if(foundViewVerticalWeight)
              _bottomVerticalWeight+=weight;
            else _topVerticalWeight+=weight;
            }
          }
        if(!foundViewVerticalWeight)
          throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value");
        }
      //
      arr.recycle();
      }

    public LayoutParams(final int width,final int height)
      {
      super(width,height);
      }

    public LayoutParams(final ViewGroup.LayoutParams source)
      {
      super(source);
      }

    public int getLeftHorizontalWeight()
      {
      return _leftHorizontalWeight;
      }

    public void setLeftHorizontalWeight(final int leftHorizontalWeight)
      {
      _leftHorizontalWeight=leftHorizontalWeight;
      }

    public int getRightHorizontalWeight()
      {
      return _rightHorizontalWeight;
      }

    public void setRightHorizontalWeight(final int rightHorizontalWeight)
      {
      if(rightHorizontalWeight<0)
        throw new IllegalArgumentException("negative weight :"+rightHorizontalWeight);
      _rightHorizontalWeight=rightHorizontalWeight;
      }

    public int getViewHorizontalWeight()
      {
      return _viewHorizontalWeight;
      }

    public void setViewHorizontalWeight(final int viewHorizontalWeight)
      {
      if(viewHorizontalWeight<0)
        throw new IllegalArgumentException("negative weight:"+viewHorizontalWeight);
      _viewHorizontalWeight=viewHorizontalWeight;
      }

    public int getTopVerticalWeight()
      {
      return _topVerticalWeight;
      }

    public void setTopVerticalWeight(final int topVerticalWeight)
      {
      if(topVerticalWeight<0)
        throw new IllegalArgumentException("negative weight :"+topVerticalWeight);
      _topVerticalWeight=topVerticalWeight;
      }

    public int getBottomVerticalWeight()
      {
      return _bottomVerticalWeight;
      }

    public void setBottomVerticalWeight(final int bottomVerticalWeight)
      {
      if(bottomVerticalWeight<0)
        throw new IllegalArgumentException("negative weight :"+bottomVerticalWeight);
      _bottomVerticalWeight=bottomVerticalWeight;
      }

    public int getViewVerticalWeight()
      {
      return _viewVerticalWeight;
      }

    public void setViewVerticalWeight(final int viewVerticalWeight)
      {
      if(viewVerticalWeight<0)
        throw new IllegalArgumentException("negative weight :"+viewVerticalWeight);
      _viewVerticalWeight=viewVerticalWeight;
      }
    }
  }

【问题讨论】:

  • 您是否有使用此布局解决的特定场景,或者您正在尝试构建一些通用的东西?请记住,LinearLayout 是一个相当复杂的布局。
  • 不,我想做一个很好的通用解决方案,它只会替换 linearLayout 的权重,但这并不是我想做类似 linearLayout 的东西。它更像是一个框架布局,您可以将任何视图放置在任何位置和任何大小,其中子级的大小和坐标是根据父级的大小设置的。看例子。
  • 使用GridLayout不是更简单吗?
  • 我不认为 gridLayout 在这里可以提供帮助,因为 gridLayout 缺少许多可以在这里完成的功能。例如,当 2 个视图可以相互叠加时,您将如何使用 gridLayout,每个视图与父视图相比具有不同的大小和位置?如果我错了,请纠正我,但 gridlayout 要求您在另一个视图之外(或下方)拥有视图,并且还需要您为每个视图准备单元格。所有这一切,而我的解决方案非常简洁明了。
  • 我认为你是在用霰弹枪杀死一只家蝇。任何布局都可以在不嵌套超过 2 或 3 层的情况下完成。 “对性能不利”警告的存在是为了阻止人们在可以通过更好的计划防止嵌套权重的情况下使用嵌套权重。嵌套的权重很难看。它们在 HTML 中也很丑陋。但是,如果您的布局要求孩子成为其父母的 %,即其父母的 %……我希望看到一种会极大影响性能并且无法使用现有布局工具解决的情况。

标签: android android-layout android-linearlayout android-layout-weight android-percent-library


【解决方案1】:

我接受了您的挑战,并尝试创建您描述的布局以响应我的评论。你说的对。出乎意料地难以完成。除此之外,我确实喜欢拍摄家蝇。所以我加入并想出了这个解决方案。

  1. 扩展现有布局类,而不是从头开始创建自己的布局类。我从 RelativeLayout 开始,但所有人都可以使用相同的方法。这使您能够在您不想操作的子视图上使用该布局的默认行为。

  2. 我在布局中添加了四个属性,称为顶部、左侧、宽度和高度。我的意图是通过允许诸如“10%”、“100px”、“100dp”等值来模仿 HTML。此时唯一接受的值是表示父级百分比的整数。 “20” = 布局的 20%。

  3. 为了获得更好的性能,我允许 super.onLayout() 执行其所有迭代,并且仅在最后一次通过时使用自定义属性操作视图。由于这些视图将独立于兄弟视图进行定位和缩放,因此我们可以在其他一切都解决后移动它们。

这里是 atts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="HtmlStyleLayout">
        <attr name="top" format="integer"/>
        <attr name="left" format="integer"/>
        <attr name="height" format="integer"/>
        <attr name="width" format="integer"/>

    </declare-styleable>
</resources>

这是我的布局类。

package com.example.helpso;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;


public class HtmlStyleLayout extends RelativeLayout{

    private int pass =0;
    @Override
      protected HtmlStyleLayout.LayoutParams generateDefaultLayoutParams()
        {
        return new HtmlStyleLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT);
        }

      @Override
      public HtmlStyleLayout.LayoutParams generateLayoutParams(final AttributeSet attrs)
        {
        return new HtmlStyleLayout.LayoutParams(getContext(),attrs);
        }

      @Override
      protected RelativeLayout.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p)
        {
        return new HtmlStyleLayout.LayoutParams(p.width,p.height);
        }

      @Override
      protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p)
        {
        final boolean isCorrectInstance=p instanceof HtmlStyleLayout.LayoutParams;
        return isCorrectInstance;
        }

    public HtmlStyleLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    public void setScaleType(View v){
        try{
            ((ImageView) v).setScaleType (ImageView.ScaleType.FIT_XY);
        }catch (Exception e){
            // The view is not an ImageView 
        }
    }


    @Override
      protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
        {
        super.onLayout(changed, l, t, r, b);           //Let the parent layout do it's thing


        pass++;                                        // After the last pass of
        final int childCount = this.getChildCount();   // the parent layout
        if(true){                        // we do our thing


            for(int i=0;i<childCount;++i)
              {
              final View v=getChildAt(i);
              final HtmlStyleLayout.LayoutParams params = (HtmlStyleLayout.LayoutParams)v.getLayoutParams();

              int newTop = v.getTop();                 // set the default value
              int newLeft = v.getLeft();               // of these to the value
              int newBottom = v.getBottom();           // set by super.onLayout() 
              int newRight= v.getRight();             
              boolean viewChanged = false;

              if(params.getTop() >= 0){
                  newTop = ( (int) ((b-t) * (params.getTop() * .01))  );
                  viewChanged = true;
              }

              if(params.getLeft() >= 0){
                  newLeft = ( (int) ((r-l) * (params.getLeft() * .01))  );
                  viewChanged = true;
              }

              if(params.getHeight() > 0){
                  newBottom = ( (int) ((int) newTop + ((b-t) * (params.getHeight() * .01)))  );
                  setScaleType(v);                        // set the scale type to fitxy
                  viewChanged = true;
              }else{
                  newBottom = (newTop + (v.getBottom() - v.getTop()));
                  Log.i("heightElse","v.getBottom()=" +
                          Integer.toString(v.getBottom())
                          + " v.getTop=" +
                          Integer.toString(v.getTop()));
              }

              if(params.getWidth() > 0){
                  newRight = ( (int) ((int) newLeft + ((r-l) * (params.getWidth() * .01)))  );
                  setScaleType(v);
                  viewChanged = true;
              }else{
                  newRight = (newLeft + (v.getRight() - v.getLeft()));
              }

                // only call layout() if we changed something
                if(viewChanged)
                    Log.i("SizeLocation",
                            Integer.toString(i) + ": "
                            + Integer.toString(newLeft) + ", "
                            + Integer.toString(newTop) + ", "
                            + Integer.toString(newRight) + ", "
                            + Integer.toString(newBottom));
                v.layout(newLeft, newTop, newRight, newBottom);
              }





            pass = 0;                                 // reset the parent pass counter
        }
        }


     public  class LayoutParams extends RelativeLayout.LayoutParams
        {

        private int top, left, width, height;
        public LayoutParams(final Context context, final AttributeSet atts) {
            super(context, atts);
            TypedArray a = context.obtainStyledAttributes(atts, R.styleable.HtmlStyleLayout);
            top =  a.getInt(R.styleable.HtmlStyleLayout_top , -1);
            left = a.getInt(R.styleable.HtmlStyleLayout_left, -1);
            width = a.getInt(R.styleable.HtmlStyleLayout_width, -1);
            height = a.getInt(R.styleable.HtmlStyleLayout_height, -1);
            a.recycle();


        }
        public LayoutParams(int w, int h) {
            super(w,h);
            Log.d("lp","2");
        }
        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
            Log.d("lp","3");
        }
        public LayoutParams(ViewGroup.MarginLayoutParams source) {
            super(source);
            Log.d("lp","4");
        }
        public int getTop(){
            return top;
        }
        public int getLeft(){
            return left;
        }
        public int getWidth(){
            return width;
        }
        public int getHeight(){
            return height;
        }
        }
}

这是一个示例活动 xml

<com.example.helpso.HtmlStyleLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:html="http://schemas.android.com/apk/res/com.example.helpso"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:scaleType="fitXY"
        android:src="@drawable/bg" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/overlay"
        html:height="10"
        html:left="13"
        html:top="18"
        html:width="23" />

</com.example.helpso.HtmlStyleLayout>

这是我用于测试的图像。

如果您没有为特定属性设置值,则将使用默认值。因此,如果您设置宽度而不是高度,图像将按宽度缩放,而 wrap_content 将按高度缩放。

Zipped project folder.

apk

我找到了错误的来源。问题是我使用布局的子计数来指示它将对 onLayout 进行多少次调用。这在旧版本的 android 中似乎并不成立。我注意到在 2.1 中 onLayout 只被调用一次。所以我改变了

if(pass == childCount){

if(true){  

它开始按预期工作。

我仍然认为只有在 super 完成后才调整布局是有益的。只需要找到一个更好的方法来知道它是什么时候。

编辑

我没有意识到您的意图是以逐像素精度将图像拼凑在一起。我通过使用双浮点精度变量而不是整数来实现您正在寻找的精度。但是,在允许图像缩放的同时,您将无法完成此操作。当图像被放大时,像素会在现有像素之间的某个间隔处添加。新像素的颜色是周围像素的加权平均值。当您独立缩放图像时,它们不会共享任何信息。结果是你总是会在接缝处有一些伪影。再加上四舍五入的结果,因为您不能有部分像素,并且您将始终具有 +/-1 像素容差。

要验证这一点,您可以在高级照片编辑软件中尝试相同的任务。我使用 PhotoShop。使用与我的 apk 中相同的图像,我将它们放在单独的文件中。我将它们垂直缩放了 168%,水平缩放了 127%。然后我将它们放在一个文件中并尝试对齐它们。结果与我的 apk 中看到的完全相同。

为了展示布局的准确性,我在我的 apk 中添加了第二个活动。在这个活动中,我没有缩放背景图像。其他一切都完全相同。结果是无缝的。

我还添加了一个按钮来显示/隐藏覆盖图像和一个在活动之间切换的按钮。

我更新了我的谷歌驱动器上的 apk 和压缩项目文件夹。您可以通过上面的链接获得它们。

【讨论】:

  • 我认为您有一个错误,导致设计师和我的设备都无法在正确的位置显示图像(我知道您使用 % 而不是 weights ,但即使在使用它时也会导致在许多情况下,图像会放在左上角)。
  • 我会在大约 3 小时后回到我的电脑后发布 APK 和项目文件夹。我并没有声称这是一个完全开发的解决方案。我已经工作了不到一天。如果它对你有帮助,那就太好了。我自己不打算用它做任何事情。
  • 抱歉粗鲁。我真的很感激这项工作。它只是没有像我预期的那样工作。
  • 我尝试在 Android 2.1 上运行它并遇到了您描述的位置错误。它在我的设备 Android 4.0.1 和 AVD Emulator 4.1 上按预期工作。我正在发布 apk 和完整的项目文件夹。这是一个非常有趣的项目。有时间我可能会回来。
  • 确定了位置错误的原因。请参阅答案中的编辑。
【解决方案2】:

在尝试了你的代码后,我才找到你提到的问题的原因,这是因为在你自定义的布局中,你只是正确地layout孩子,但是你忘记了测量你的孩子正确,这将直接影响绘图层次,所以只需添加以下代码,它对我有用。

    @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSize = MeasureSpec.getSize(widthMeasureSpec)-this.getPaddingRight()-this.getPaddingRight();
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);

    int heightSize = MeasureSpec.getSize(heightMeasureSpec)-this.getPaddingTop()-this.getPaddingBottom();
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    if(heightMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.UNSPECIFIED)
        throw new IllegalArgumentException("the layout must have a exact size");

    for (int i = 0; i < this.getChildCount(); ++i) {
        View child = this.getChildAt(i);
        LayoutParams lp = (LayoutParams)child.getLayoutParams();
        int width = lp._viewHorizontalWeight * widthSize/(lp._leftHorizontalWeight+lp._rightHorizontalWeight+lp._viewHorizontalWeight);
        int height =  lp._viewVerticalWeight * heightSize/(lp._topVerticalWeight+lp._bottomVerticalWeight+lp._viewVerticalWeight);
        child.measure(width | MeasureSpec.EXACTLY,  height | MeasureSpec.EXACTLY);
    }

    this.setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));

}

【讨论】:

  • 你已经修复了我的代码,但是当我进行我提供的“拼图测试”时,我仍然有奇怪的额外拼接空间。因此,例如,如果您为 Galaxy s3 拍摄 720x1280 图像并将其全屏显示,然后拍摄该图像的部分图像(从原始图像中裁剪)并尝试将它们放在新布局中,它可能会显示您已附加的部分附近的黑线。即使没有,它也会显示在其他屏幕上。有办法解决吗?
  • 我刚试了,没看到黑线?你在哪里看到他们? “它将显示在其他屏幕上”是什么意思?
  • 我想我明白你的意思了,这不是你的布局问题,因为ImageView的默认缩放类型是FIT_CENTER,所以你会看到那些黑色拼接,尝试看看设置缩放类型为FIT_XY ,一切都会好起来的。
  • 不,即使将 scaleType 设置为 fit_xy 并且将 adjustViewBounds 设置为 false,也可以显示针迹。您可以通过更改方向在您的设备上进行测试。
【解决方案3】:

现在有一个比我制作的自定义布局更好的解决方案:

PercentRelativeLayout

教程可以在here找到,repo可以在here找到。

示例代码:

<android.support.percent.PercentRelativeLayout
         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"/>
     <ImageView
         app:layout_widthPercent="50%"
         app:layout_heightPercent="50%"
         app:layout_marginTopPercent="25%"
         app:layout_marginLeftPercent="25%"/>
 </android.support.percent.PercentFrameLayout/>

或:

 <android.support.percent.PercentFrameLayout
         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"/>
     <ImageView
         app:layout_widthPercent="50%"
         app:layout_heightPercent="50%"
         app:layout_marginTopPercent="25%"
         app:layout_marginLeftPercent="25%"/>
 </android.support.percent.PercentFrameLayout/>

我想知道它是否可以处理我在这里展示的问题。

【讨论】:

  • 但是……但是……!但 !但是为什么现在才……?我刚刚发现了这个支持库......我想告诉每个 android 开发者它的存在......
【解决方案4】:

我建议使用以下优化:

<FrameLayout 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" android:gravity="center">


    <TextView android:layout_width="wrap_content"
      android:layout_height="wrap_content" android:text="@string/hello_world"
      android:background="#ffff0000" android:gravity="center"
      android:textSize="20dp" android:textColor="#ff000000" />

</FrameLayout>

或使用http://developer.android.com/reference/android/widget/LinearLayout.html#attr_android:weightSum

或将 TableLayout 与 layout_weight 一起用于行和列

或使用 GridLayout。

【讨论】:

  • 这不是我要求的。我知道所有这些解决方案,但没有一个提供相对大小和位置。唯一类似的解决方案是使用线性布局的权重,正如我所展示的,这是一个很长的解决方案,可读性差,性能也差。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-29
  • 1970-01-01
相关资源
最近更新 更多