(转)Android Support Percent百分比布局

一、概述

周末游戏打得过猛,于是周天熬夜码代码,周一早上浑浑噩噩的发现 android-percent-support-lib-sample(https://github.com/JulienGenoud/android-percent-support-lib-sample) 这个项目,Google终于开始支持百分比的方式布局了,瞬间脉动回来,啊咧咧。对于这种历史性的时刻,不出篇博客难以表达我内心的激动。

还记得不久前,发了篇博客:Android 屏幕适配方案,这篇博客以Web页面设计引出一种适配方案,最终的目的就是可以通过百分比控制控件的大小。当然了,存在一些问题,比如:

  • 对于没有考虑到屏幕尺寸,可能会出现意外的情况;
  • apk的大小会增加;

当然了android-percent-support这个库,基本可以解决上述问题,是不是有点小激动,稍等,我们先描述下这个support-lib。

这个库提供了:

  • 两种布局供大家使用:

    PercentRelativeLayout 、 PercentFrameLayout ,通过名字就可以看出,这是继承自 FrameLayout 和 RelativeLayout 两个容器类;

  • 支持的属性有:

layout_widthPercent 、 layout_heightPercent 、

layout_marginPercent 、 layout_marginLeftPercent 、

layout_marginTopPercent 、 layout_marginRightPercent 、

layout_marginBottomPercent 、 layout_marginStartPercent 、layout_marginEndPercent 。

可以看到支持宽高,以及margin。

也就是说,大家只要在开发过程中使用 PercentRelativeLayout 、PercentFrameLayout 替换 FrameLayout 、 RelativeLayout 即可。

是不是很简单,不过貌似没有LinearLayout,有人会说LinearLayout有weight属性呀。但是,weight属性只能支持一个方向呀~~哈,没事,刚好给我们一个机会去自定义一个 PercentLinearLayout 。

好了,本文分为3个部分:

  • PercentRelativeLayout 、 PercentFrameLayout 的使用
  • 对上述控件源码分析
  • 自定义 PercentLinearLayout

二、使用

关于使用,其实及其简单,并且github上也有例子, android-percent-support-lib-sample(https://github.com/JulienGenoud/android-percent-support-lib-sample) 。我们就简单过一下:

首先记得在build.gradle添加:

[Java] 纯文本查看 复制代码

?


1

compile ‘com.android.support:percent:22.2.0‘

(一)PercentFrameLayout

[XML] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

<?xml version="1.0" encoding="utf-8"?>

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

    <TextView

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:layout_gravity="left|top"

  android:background="#44ff0000"

  android:text="width:30%,height:20%"

  app:layout_heightPercent="20%"

  android:gravity="center"

  app:layout_widthPercent="30%"/>

    <TextView

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:layout_gravity="right|top"

  android:gravity="center"

  android:background="#4400ff00"

  android:text="width:70%,height:20%"

  app:layout_heightPercent="20%"

  app:layout_widthPercent="70%"/>

    <TextView

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:layout_gravity="bottom"

  android:background="#770000ff"

  android:text="width:100%,height:10%"

  android:gravity="center"

  app:layout_heightPercent="10%"

  app:layout_widthPercent="100%"/>

</android.support.percent.PercentFrameLayout>

3个TextView,很简单,直接看效果图:

(二) PercentRelativeLayout

[XML] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

<?xml version="1.0" encoding="utf-8"?>

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

    android:clickable="true">

    <TextView

  android:id="@+id/row_one_item_one"

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:layout_alignParentTop="true"

  android:background="#7700ff00"

  android:text="w:70%,h:20%"

  android:gravity="center"

  app:layout_heightPercent="20%"

  app:layout_widthPercent="70%"/>

    <TextView

  android:id="@+id/row_one_item_two"

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:layout_toRightOf="@+id/row_one_item_one"

  android:background="#396190"

  android:text="w:30%,h:20%"

  app:layout_heightPercent="20%"

  android:gravity="center"

  app:layout_widthPercent="30%"/>

    <ImageView

  android:id="@+id/row_two_item_one"

  android:layout_width="match_parent"

  android:layout_height="0dp"

  android:src="@drawable/tangyan"

  android:scaleType="centerCrop"

  android:layout_below="@+id/row_one_item_one"

  android:background="#d89695"

  app:layout_heightPercent="70%"/>

    <TextView

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:layout_below="@id/row_two_item_one"

  android:background="#770000ff"

  android:gravity="center"

  android:text="width:100%,height:10%"

  app:layout_heightPercent="10%"

  app:layout_widthPercent="100%"/>

</android.support.percent.PercentRelativeLayout>

ok,依然是直接看效果图:

使用没什么好说的,就是直观的看一下。

三、源码分析

其实细想一下,Google只是对我们原本熟悉的RelativeLayout和FrameLayout进行的功能的扩展,使其支持了percent相关的属性。

那么,我们考虑下,如果是我们添加这种扩展,我们会怎么做:

  • 通过LayoutParams获取child设置的percent相关属性的值
  • onMeasure的时候,将child的width,height的值,通过获取的自定义属性的值进行计算(eg:容器的宽 * fraction ),计算后传入给child.measure(w,h);

ok,有了上面的猜想,我们直接看 PercentFrameLayout 的源码。

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

public class PercentFrameLayout extends FrameLayout {

  private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);

  //省略了,两个构造方法

  public PercentFrameLayout(Context context, AttributeSet attrs) {

    super(context, attrs);

  }

  @Override

  public LayoutParams generateLayoutParams(AttributeSet attrs) {

    return new LayoutParams(getContext(), attrs);

  }

  @Override

  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (mHelper.handleMeasuredStateTooSmall()) {

      super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }

  }

  @Override

  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

    super.onLayout(changed, left, top, right, bottom);

    mHelper.restoreOriginalParams();

  }

  public static class LayoutParams extends FrameLayout.LayoutParams

      implements PercentLayoutHelper.PercentLayoutParams {

    private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;

    public LayoutParams(Context c, AttributeSet attrs) {

      super(c, attrs);

      mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);

    }

    //省略了一些代码...

    @Override

    public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {

      return mPercentLayoutInfo;

    }

    @Override

    protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {

      PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);

    }

  }

}

代码是相当的短,可以看到 PercentFrameLayout 里面首先重写了generateLayoutParams方法,当然了,由于支持了一些新的layout_属性,那么肯定需要定义对应的LayoutParams。

(一)percent相关属性的获取

可以看到PercentFrameLayout.LayoutParams在原有的FrameLayout.LayoutParams基础上,实现了PercentLayoutHelper.PercentLayoutParams接口。

这个接口很简单,只有一个方法:

[Java] 纯文本查看 复制代码

?


1

2

3

public interface PercentLayoutParams {

        PercentLayoutInfo getPercentLayoutInfo();

    }

而,这个方法的实现呢,也只有一行: return mPercentLayoutInfo; ,那么这个mPercentLayoutInfo在哪完成赋值呢?

看PercentFrameLayout.LayoutParams的构造方法:

[Java] 纯文本查看 复制代码

?


1

2

3

4

public LayoutParams(Context c, AttributeSet attrs) {

            super(c, attrs);

            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);

        }

可以看到,将attrs传入给getPercentLayoutInfo方法,那么不用说,这个方法的内部,肯定是获取自定义属性的值,然后将其封装到PercentLayoutInfo对象中,最后返回。

代码如下:

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

public static PercentLayoutInfo getPercentLayoutInfo(Context context,

    AttributeSet attrs) {

  PercentLayoutInfo info = null;

  TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);

  float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,

      -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent width: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.widthPercent = value;

  }

  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent height: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.heightPercent = value;

  }

  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent margin: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.leftMarginPercent = value;

    info.topMarginPercent = value;

    info.rightMarginPercent = value;

    info.bottomMarginPercent = value;

  }

  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,

      -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent left margin: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.leftMarginPercent = value;

  }

  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,

      -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent top margin: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.topMarginPercent = value;

  }

  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,

      -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent right margin: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.rightMarginPercent = value;

  }

  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,

      -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent bottom margin: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.bottomMarginPercent = value;

  }

  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,

      -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent start margin: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.startMarginPercent = value;

  }

  value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,

      -1f);

  if (value != -1f) {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {

      Log.v(TAG, "percent end margin: " + value);

    }

    info = info != null ? info : new PercentLayoutInfo();

    info.endMarginPercent = value;

  }

  array.recycle();

  if (Log.isLoggable(TAG, Log.DEBUG)) {

    Log.d(TAG, "constructed: " + info);

  }

  return info;

}

是不是和我们平时的取值很类似,所有的值最终封装到PercentLayoutInfo对象中。

ok,到此我们的属性获取就介绍完成,有了这些属性,是不是onMeasure里面要进行使用呢?

(二) onMeasue中重新计算child的尺寸

[Java] 纯文本查看 复制代码

?


1

2

3

4

5

6

7

8

@Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (mHelper.handleMeasuredStateTooSmall()) {

            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        }

    }

可以看到onMeasure中的代码页很少,看来核心的代码都被封装在mHelper的方法中,我们直接看mHelper.adjustChildren方法。

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

/**

   * Iterates over children and changes their width and height to one calculated from percentage

   * values.

   * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.

   * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.

   */

  public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {

    //...

    int widthHint = View.MeasureSpec.getSize(widthMeasureSpec);

    int heightHint = View.MeasureSpec.getSize(heightMeasureSpec);

    for (int i = 0, N = mHost.getChildCount(); i < N; i++) {

      View view = mHost.getChildAt(i);

      ViewGroup.LayoutParams params = view.getLayoutParams();

      if (params instanceof PercentLayoutParams) {

        PercentLayoutInfo info =

            ((PercentLayoutParams) params).getPercentLayoutInfo();

        if (Log.isLoggable(TAG, Log.DEBUG)) {

          Log.d(TAG, "using " + info);

        }

        if (info != null) {

          if (params instanceof ViewGroup.MarginLayoutParams) {

            info.fillMarginLayoutParams((ViewGroup.MarginLayoutParams) params,

                widthHint, heightHint);

          } else {

            info.fillLayoutParams(params, widthHint, heightHint);

          }

        }

      }

    }

  }

通过注释也能看出,此方法中遍历所有的孩子,通过百分比的属性重新设置其宽度和高度。

首先在widthHint、heightHint保存容器的宽、高,然后遍历所有的孩子,判断其LayoutParams是否是PercentLayoutParams类型,如果是,通过params.getPercentLayoutInfo拿出info对象。

是否还记得,上面的分析中,PercentLayoutInfo保存了percent相关属性的值。

如果info不为null,则判断是否需要处理margin;我们直接看fillLayoutParams方法(处理margin也是类似的)。

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

/** * Fills {[url=home.php?mod=space&uid=29280]@code[/url] ViewGroup.LayoutParams} dimensions based on percentage values.

 */

public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,

    int heightHint) {

  // Preserve the original layout params, so we can restore them after the measure step.

  mPreservedParams.width = params.width;

  mPreservedParams.height = params.height;

  if (widthPercent >= 0) {

    params.width = (int) (widthHint * widthPercent);

  }

  if (heightPercent >= 0) {

    params.height = (int) (heightHint * heightPercent);

  }

  if (Log.isLoggable(TAG, Log.DEBUG)) {

    Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");

  }

}

首先保存原本的width和height,然后重置params的width和height为 (int) (widthHint * widthPercent) 和 (int) (heightHint * heightPercent);。

到此,其实我们的百分比转换就结束了,理论上就已经实现了对于百分比的支持,不过Google还考虑了一些细节。

我们回到onMeasure方法:

[Java] 纯文本查看 复制代码

?


1

2

3

4

5

6

7

8

@Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (mHelper.handleMeasuredStateTooSmall()) {

            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        }

    }

下面还有个mHelper.handleMeasuredStateTooSmall的判断,也就是说,如果你设置的百分比,最终计算出来的MeasuredSize过小的话,会进行一些操作。代码如下:

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public boolean handleMeasuredStateTooSmall() {

  boolean needsSecondMeasure = false;

  for (int i = 0, N = mHost.getChildCount(); i < N; i++) {

    View view = mHost.getChildAt(i);

    ViewGroup.LayoutParams params = view.getLayoutParams();

    if (Log.isLoggable(TAG, Log.DEBUG)) {

      Log.d(TAG, "should handle measured state too small " + view + " " + params);

    }

    if (params instanceof PercentLayoutParams) {

      PercentLayoutInfo info =

          ((PercentLayoutParams) params).getPercentLayoutInfo();

      if (info != null) {

        if (shouldHandleMeasuredWidthTooSmall(view, info)) {

          needsSecondMeasure = true;

          params.width = ViewGroup.LayoutParams.WRAP_CONTENT;

        }

        if (shouldHandleMeasuredHeightTooSmall(view, info)) {

          needsSecondMeasure = true;

          params.height = ViewGroup.LayoutParams.WRAP_CONTENT;

        }

      }

    }

  }

  if (Log.isLoggable(TAG, Log.DEBUG)) {

    Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);

  }

  return needsSecondMeasure;

}

首先遍历所有的孩子,拿出孩子的layoutparams,如果是PercentLayoutParams实例,则取出info。如果info不为null,调用 shouldHandleMeasuredWidthTooSmall 判断:

[Java] 纯文本查看 复制代码

?


1

2

3

4

5

private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {

        int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;

        return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 &&

                info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;

    }


里就是判断,如果你设置的measuredWidth或者measureHeight过小的话,并且你在布局文件中layout_w/h
设置的是WRAP_CONTENT的话,将params.width / height=
ViewGroup.LayoutParams.WRAP_CONTENT,然后重新测量。

哈,onMeasure终于结束了~~~现在我觉得应该代码结束了吧,尺寸都设置好了,还需要干嘛么,but,你会发现onLayout也重写了,我们又不改变layout规则,在onLayout里面干什么毛线:

[Java] 纯文本查看 复制代码

?


1

2

3

4

5

@Override

    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

        super.onLayout(changed, left, top, right, bottom);

        mHelper.restoreOriginalParams();

    }

继续看 mHelper.restoreOriginalParams

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

/**   * Iterates over children and restores their original dimensions that were changed for

   * percentage values. Calling this method only makes sense if you previously called

   * {@link PercentLayoutHelper#adjustChildren(int, int)}.

   */

  public void restoreOriginalParams() {

    for (int i = 0, N = mHost.getChildCount(); i < N; i++) {

      View view = mHost.getChildAt(i);

      ViewGroup.LayoutParams params = view.getLayoutParams();

      if (Log.isLoggable(TAG, Log.DEBUG)) {

        Log.d(TAG, "should restore " + view + " " + params);

      }

      if (params instanceof PercentLayoutParams) {

        PercentLayoutInfo info =

            ((PercentLayoutParams) params).getPercentLayoutInfo();

        if (Log.isLoggable(TAG, Log.DEBUG)) {

          Log.d(TAG, "using " + info);

        }

        if (info != null) {

          if (params instanceof ViewGroup.MarginLayoutParams) {

            info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);

          } else {

            info.restoreLayoutParams(params);

          }

        }

      }

    }

  }

噗,原来是重新恢复原本的尺寸值,也就是说onMeasure里面的对值进行了改变,测量完成后。在这个地方,将值又恢复成如果布局文件中的值,上面写的都是0。恢复很简单:


1

2

3

4

public void restoreLayoutParams(ViewGroup.LayoutParams params) {

            params.width = mPreservedParams.width;

            params.height = mPreservedParams.height;

        }

你应该没有忘在哪存的把~忘了的话,麻烦Ctrl+F ‘mPreservedParams.width’ 。

也就是说,你去打印上面写法,布局文件中view的 v.getLayoutParams().width,这个值应该是0。

这里感觉略微不爽~这个0没撒用处呀,还不如不重置~~

好了,到此就分析完了,其实主要就几个步骤:

  • LayoutParams中属性的获取
  • onMeasure中,改变params.width为百分比计算结果,测量
  • 如果测量值过小且设置的w/h是wrap_content,重新测量
  • onLayout中,重置params.w/h为布局文件中编写的值

可以看到,有了RelativeLayout、FrameLayout的扩展,竟然没有LinearLayout几个意思。好在,我们的核心代码都由 PercentLayoutHelper 封装了,自己扩展下LinearLayout也不复杂。

三、实现PercentLinearlayout

可能有人会说,有了weight呀,但是weight能做到宽、高同时百分比赋值嘛?

好了,代码很简单,如下:

(一)PercentLinearLayout

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

package com.juliengenoud.percentsamples;

import android.content.Context;

import android.content.res.TypedArray;

import android.support.percent.PercentLayoutHelper;

import android.util.AttributeSet;

import android.view.ViewGroup;

import android.widget.LinearLayout;

/**

 * Created by zhy on 15/6/30.

 */

public class PercentLinearLayout extends LinearLayout

{

  private PercentLayoutHelper mPercentLayoutHelper;

  public PercentLinearLayout(Context context, AttributeSet attrs)

  {

    super(context, attrs);

    mPercentLayoutHelper = new PercentLayoutHelper(this);

  }

  @Override

  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

  {

    mPercentLayoutHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (mPercentLayoutHelper.handleMeasuredStateTooSmall())

    {

      super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }

  }

  @Override

  protected void onLayout(boolean changed, int l, int t, int r, int b)

  {

    super.onLayout(changed, l, t, r, b);

    mPercentLayoutHelper.restoreOriginalParams();

  }

  @Override

  public LayoutParams generateLayoutParams(AttributeSet attrs)

  {

    return new LayoutParams(getContext(), attrs);

  }

  public static class LayoutParams extends LinearLayout.LayoutParams

      implements PercentLayoutHelper.PercentLayoutParams

  {

    private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;

    public LayoutParams(Context c, AttributeSet attrs)

    {

      super(c, attrs);

      mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);

    }

    @Override

    public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()

    {

      return mPercentLayoutInfo;

    }

    @Override

    protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)

    {

      PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);

    }

    public LayoutParams(int width, int height) {

      super(width, height);

    }

    public LayoutParams(ViewGroup.LayoutParams source) {

      super(source);

    }

    public LayoutParams(MarginLayoutParams source) {

      super(source);

    }

  }

}

如果你详细看了上面的源码分析,这个代码是不是没撒解释的了~

(二)测试布局

[XML] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

<?xml version="1.0" encoding="utf-8"?>

<com.juliengenoud.percentsamples.PercentLinearLayout

    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"

    android:orientation="vertical">

    <TextView

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:background="#ff44aacc"

  android:text="width:60%,height:5%"

  android:textColor="#ffffff"

  app:layout_heightPercent="5%"

  app:layout_marginBottomPercent="5%"

  app:layout_widthPercent="60%"/>

    <TextView

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:background="#ff4400cc"

  android:gravity="center"

  android:textColor="#ffffff"

  android:text="width:70%,height:10%"

  app:layout_heightPercent="10%"

  app:layout_marginBottomPercent="5%"

  app:layout_widthPercent="70%"/>

    <TextView

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:background="#ff44aacc"

  android:gravity="center"

  android:text="width:80%,height:15%"

  android:textColor="#ffffff"

  app:layout_heightPercent="15%"

  app:layout_marginBottomPercent="5%"

  app:layout_widthPercent="80%"/>

    <TextView

  android:layout_width="0dp"

  android:layout_height="0dp"

  android:background="#ff4400cc"

  android:gravity="center"

  android:text="width:90%,height:5%"

  android:textColor="#ffffff"

  app:layout_heightPercent="20%"

  app:layout_marginBottomPercent="10%"

  app:layout_widthPercent="90%"/>

    <TextView

  android:layout_width="match_parent"

  android:layout_height="0dp"

  android:background="#ff44aacc"

  android:gravity="center"

  android:text="width:100%,height:25%"

  android:textColor="#ffffff"

  app:layout_heightPercent="25%"

  app:layout_marginBottomPercent="5%"

  />

</com.juliengenoud.percentsamples.PercentLinearLayout>

我们纵向排列的几个TextView,分别设置宽/高都为百分比,且之间的间隔为5%p。

(三)效果图

ok,到此,我们使用、源码分析、扩展PercentLinearLayout就结束了。

添加PercentLinearLayout后的地址: https://github.com/hongyangAndroid/android-percent-support-lib-sample

~~have a nice day ~~

原文地址:http://blog.csdn.net/lmj623565791/article/details/46695347

时间: 2024-08-02 20:31:14

(转)Android Support Percent百分比布局的相关文章

Android Support库百分比布局

之前写过一篇屏幕适配的文章Android 屏幕适配最佳实践,里面提到了类似百分比布局的东西,但是该方法缺点很明显,就会增加很多无用的数据,导致apk包变大. 而谷歌的support库中,增加了一个叫做percent库,该库在如图目录下,如果没有,请使用sdk manager更新至最新 在使用前,我们先看下这个库有哪些类 很显里面有一个FrameLayout布局的子类和RelativeLayout布局的子类,此外还有一个Helper类,这个Helper类主要是完成百分比的测量工作,里面有一个接口P

Android百分比布局支持库介绍——com.android.support:percent

在此之前,相信大家都已经对Android API所提供的布局方式非常熟悉了.也许在接触Android的时候都有过这样的想法,如果可以按照百分比的方式进行界面布局,这样适配各种屏幕就简单多了吧!!以前的一个小梦想,现在终于得以实现,谷歌正式提供百分比布局支持库(percent-support-lib). <ignore_js_op> 获取支持库: 使用Android studio在build.gradle添加以下信息就可以获取支持库,当然了,如果你没有下载到该支持库会提示你下载. [AppleS

Android添加百分比布局库时显示Failed to resolve: com.android.support.percent:问题

这是看第一行代码中遇到的问题,要添加百分比布局库的依赖时要在app下的bulid.gradle添加以下代码 implementation fileTree(dir:'libs',include:['*.jar'])implementation 'com.android.support:appcompat-v7:28.0.0'implementation 'com.android.support:percent:28.0.0' testCompile 'junit:junit:4.12' 注意这一

android support Percent支持库开发

Android的布局支持百分比的设置进行开发,来学习如何去实现它,不过看起来会像网页的设置,比如宽度的设置属性是`layout_widthPercent`.在此之前,我们一般都会设置Linearlayout的weight权重来实现布局间的比例大小. Percent support Library提供了两个新的类: 1.PercentRelativeLayout 2.PercentFrameLayout 创建新项目 创建一个新的项目来测试,修改`build.gradle`,需要引入以下库 `app

Android适配之百分比的简单用法

我是一枚IT界的小学生,本文也是针对百分比的简单用法以及总结,如果想要深入研究请移步至大神的博客,下面开始: 首先,需要添加com.android.support:percent:24.1.1 包,版本随意. dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.1.1'

android PercentRelativeLayout 支持百分比来设置控件的宽高

Android 最终官方支持按百分比来设置控件的宽高了. 我们先来看看效果:       看一下布局: PercentRelativeLayout <android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"

百分比布局的使用

今天有时间捣鼓了一下这个东西,和大家分享一下. 官方提供的包里,关于百分比布局有两个,如下: 就是PercentFrameLayout和PercentRelativeLayout,我们今天就来说说这两个百分比布局的使用吧. 1.添加依赖库 本文Demo使用Android Studio来完成,所以直接在Gradle文件中添加下面一行即可. compile 'com.android.support:percent:23.1.0' 2.在布局文件中使用 <android.support.percent

百分比布局 (翻译,体验)解决布局问题,又一种体验

转载请注明出处:王亟亟的大牛之路 我们过去经常使用传统的RelativeLayout,LinearLayout,FrameLayout.以及一些较新的诸如可拉伸的CollapsingToolbarLayout等等都已经多多少少的在项目中使用,早前就已经知晓了百分比布局percent,但是一直没想到去看,去试验相关的内容,正好今天想到了就写一篇关于他的(貌似是本周的第一篇) 安利下自己的整合库:https://github.com/ddwhan0123/Useful-Open-Source-And

《第一行代码》添加百分比布局库依赖问题

错误条件 按照<第一行代码>中操作,给app模块添加依赖:compile 'com.android.support.percent:24.2.1',gradle同步的时候报出以下错误: ERROR: Failed to resolve: com.android.support.percent:24.2.1: Affected Modules: app WARNING: Configuration 'compile' is obsolete and has been replaced with