替代linearLayout权重机制的方法

18

背景:

  • 谷歌建议避免使用嵌套的加权线性布局,因为这会影响性能。
  • 使用嵌套的加权线性布局不易阅读、编写和维护。
  • 至今仍没有很好的替代方法来放置大小为可用空间百分比的视图。唯一的解决方案是使用权重和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;
      }
    }
  }

1
我认为你在用猎枪打苍蝇。任何布局都可以在不嵌套超过2或3层的情况下完成。 "对性能不利"的警告存在是为了阻止人们在可以通过更好的规划避免的情况下使用嵌套权重。 嵌套权重很丑陋。 在HTML中也是如此。 但是,如果您的布局要求子元素是其父元素的百分比,而该父元素又是其父元素的百分比... 我想看到一个会极大影响性能并且无法使用现有布局工具解决的情况。 - Jason Hessley
是的,我知道我发现的是以百分比设置视图宽度和高度的方法。我知道有一些罕见的情况需要这样做,但这确实非常有效。 - Muhammad Babar
我总是使用嵌套加权...如果不使用"嵌套加权",那将非常非常困难。 - Shirish Herwade
@Android开发者,有没有人想要百分比支持库的演示?请访问http://code2concept.blogspot.in/2015/08/android-percent-support-lib-sample.html。 - Nitesh Tiwari
@nitesh,我已经在上一篇帖子中展示了它,包括代码,链接在这里:https://dev59.com/HOo6XIcBkEYKwwoYPSDf#31792245。 - android developer
显示剩余9条评论
4个回答

8

我接受了你的挑战,并尝试根据我的评论创建你描述的布局。你是对的。这实际上很难完成。除此之外,我喜欢打苍蝇。所以我加入并想出了这个解决方案。

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

  2. 我添加了四个名为top、left、width和height的布局属性。我的意图是通过允许诸如“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>

这是我用于测试的图像。

bg enter image description here

如果您没有为特定属性设置值,则会使用其默认值。因此,如果您设置了宽度但未设置高度,则图像将按比例缩放,并且高度将自适应内容。

压缩的项目文件夹。

apk

我找到了错误的来源。问题是我使用布局的子项计数作为调用onLayout的次数指示器。这在旧版本的Android中似乎不成立。我注意到在2.1中,只调用一次onLayout。所以我进行了更改。

if(pass == childCount){

if(true){  

然后它开始按预期工作。

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

编辑

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

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

为了展示布局的准确性,我在我的apk中添加了第二个活动。在这个活动中,我没有缩放背景图片。其他所有内容都完全相同。结果是无缝的。
我还添加了一个按钮来显示/隐藏叠加图像和一个用于在活动之间切换的按钮。
我更新了我的谷歌云盘上的apk和压缩项目文件夹。您可以通过上面的链接获取它们。

我将在大约3个小时后回到电脑时发布APK和项目文件夹。我不会声称这是一个完全开发好的解决方案。我只花了不到一天的时间来研究它。如果它对你有所帮助,那就太好了。我自己没有计划再做任何事情。 - Jason Hessley
抱歉我之前说话有些冒犯。我非常感谢你的工作,只是它没有达到我的预期效果。 - android developer
已经确定了位置误差的原因。请查看答案中的编辑。 - Jason Hessley
我仍然不明白黑/白线是从哪里来的。我认为imageView内容本身的缩放没有正确完成。即使对于这个相对较高的分辨率,双精度是否足够?如果我保持背景图像和覆盖图像的纵横比,这会被修复吗?在OpenGL上是否存在相同的问题?顺便说一句,现在我不知道应该把奖项给谁,是给做了一个全新项目并解释了很多的你,还是给实际修复了我的代码并达到了相同结果的jaredzhang。 - android developer
你应该能够将两个图像的数据添加到画布中。然后,你可以按照自己的意愿缩放画布以适应屏幕。由于在缩放之前,两个图像都是画布的一部分,所以缩放会很平滑。但这不再是“布局”,而更像是一个图像处理器。使用情况也会非常不同。 - Jason Hessley
显示剩余6条评论

4

在尝试了您的代码后,我发现您提到的问题的原因是因为在您自定义的布局中,您只适当地布置了子项,但您忘记了适当地测量您的子项,这将直接影响绘图层次结构,因此只需添加下面的代码,它对我有效。

    @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));

}

你已经修复了我的代码,但是当我进行“拼图测试”时,仍然存在奇怪的额外缝合空间。例如,如果您使用720x1280像素的图像来为Galaxy S3手机全屏显示,并从原始图像中裁剪出部分图像并尝试将它们放在新布局中,可能会在您附加的那一块附近显示黑线。即使没有,它也会在其他屏幕上显示。有可能修复吗? - android developer
我刚试了一下,没有看到任何黑线?你在哪里看到它们?“它将显示在其他屏幕上”是什么意思? - jaredzhang
我想我明白你的意思了,这不是你的布局问题,因为ImageView的默认缩放类型是FIT_CENTER,所以你会看到那些黑色的拼接线,尝试将缩放类型设置为FIT_XY,一切都会好起来的。 - jaredzhang
不,即使将scaleType设置为fit_xy,并且adjustViewBounds设置为false,也可以显示出线缝。您可以通过更改设备方向在设备上进行测试。 - android developer

1

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

PercentRelativeLayout

教程可以在这里找到,存储库可以在这里找到。

示例代码:

<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/>

不过我不确定它是否能处理我在这里展示的问题。


1
但是...但是...!但是!为什么现在才...?我刚刚发现了这个支持库...我想告诉每个Android开发者它的存在... - kmas

-1

我建议使用以下优化:

<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。


这不是我要求的。我知道所有这些解决方案,但没有一个提供相对大小和位置。唯一类似的解决方案是使用LinearLayout的权重,正如我所展示的那样,这是一个非常冗长的解决方案,可读性差且性能不佳。 - android developer

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