(转)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添加:
1
|
compile 'com.android.support:percent:22.2.0' |
(一)PercentFrameLayout
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 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
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 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 的源码。
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接口。
这个接口很简单,只有一个方法:
1
2
3
|
public interface PercentLayoutParams { PercentLayoutInfo getPercentLayoutInfo(); } |
而,这个方法的实现呢,也只有一行: return mPercentLayoutInfo; ,那么这个mPercentLayoutInfo在哪完成赋值呢?
看PercentFrameLayout.LayoutParams的构造方法:
1
2
3
4
|
public LayoutParams(Context c, AttributeSet attrs) { super (c, attrs); mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs); } |
可以看到,将attrs传入给getPercentLayoutInfo方法,那么不用说,这个方法的内部,肯定是获取自定义属性的值,然后将其封装到PercentLayoutInfo对象中,最后返回。
代码如下:
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的尺寸
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方法。
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也是类似的)。
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方法:
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过小的话,会进行一些操作。代码如下:
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 判断:
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里面干什么毛线:
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
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
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); } } } |
如果你详细看了上面的源码分析,这个代码是不是没撒解释的了~
(二)测试布局
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 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
(转)Android Support Percent百分比布局的更多相关文章
- Android Support库百分比布局
之前写过一篇屏幕适配的文章Android 屏幕适配最佳实践,里面提到了类似百分比布局的东西,可是该方法缺点非常明显,就会添加非常多没用的数据,导致apk包变大. 而谷歌的support库中,添加了一个 ...
- Android百分比布局支持库介绍——com.android.support:percent(转)
转载自http://www.apkbus.com/forum.php?mod=viewthread&tid=244752&extra=&_dsign=0b699c42 在此之前 ...
- Android添加百分比布局库时显示Failed to resolve: com.android.support.percent:问题
这是看第一行代码中遇到的问题,要添加百分比布局库的依赖时要在app下的bulid.gradle添加以下代码 implementation fileTree(dir:'libs',include:['* ...
- android support Percent支持库开发
Android的布局支持百分比的设置进行开发,来学习如何去实现它,不过看起来会像网页的设置,比如宽度的设置属性是`layout_widthPercent`.在此之前,我们一般都会设置Linearlay ...
- Android Studio com.android.support:percent 导入错误 - 转
看第一行代码(第二版的)书,讲了一个关于PercentFrameLayout和PercentRelativeLayout的部分,书上在build.gradle中导入了com.android.suppo ...
- Android 增强版百分比布局库 为了适配而扩展
转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/46767825: 本文出自:[张鸿洋的博客] 一 概述 上周一我们发布了Andr ...
- 屏幕适配1(Android 增强版百分比布局库 为了适配而扩展)
转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/46767825: 本文出自:[张鸿洋的博客] 这篇博文写得太好了,让我很激动,分 ...
- Android 屏幕适配(一)百分比布局库(percent-support-lib) 解析与扩展
转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/46695347: 本文出自:[张鸿洋的博客] 一.概述 周末游戏打得过猛,于是周 ...
- Android百分比布局支持库(android-percent-support)
Android中提供了五种布局,其中用的最多的就是:LinearLayout, RelativeLayout 和 FrameLayout这三种布局,在对某一界面进行布局时最先想到也是通过这三种来布局的 ...
随机推荐
- EasyUI设置复选框单选操作
Html 代码: <td align="left" colspan="2"> <input type=& ...
- 重构7-Rename(method,class,parameter)
这是我最常用也是最有用的重构之一.我们对方法/类/参数的命名往往不那么合适,以至于误导阅读者对于方法/类/参数功能的理解.这会造成阅读者的主观臆断,甚至引入bug.这个重构看起来简单,但却十分重要. ...
- 虚拟机的MAC地址分配与修改
虚拟世界的MAC地址 先看一下真实世界的MAC地址是如何分配,如何保证没有重复的.每块网卡都有一个MAC地址,MAC地址是一个6字节.也即48bit的数据.前3字节称为OUI ,是由IEEE组织注册给 ...
- hdu 4010 Query on The Trees LCT
支持:1.添加边 x,y2.删边 x,y3.对于路径x,y上的所有节点的值加上w4.询问路径x,y上的所有节点的最大权值 分析:裸的lct...rev忘了清零死循环了两小时... 1:就是link操作 ...
- Unity之定时调用
1.Invoke(string methodName,float time) 在一定时间调用methodName函数 using UnityEngine; using System.Collectio ...
- 发布后500访问错误 —— dll引用错误
System.Net.Http相关dll
- Sharepoint 2013 安装部署系列篇 第一篇 -- 系统集群安装
这部分讲述怎样配置两台服务器作为sql集群. 准备 *你需要两个网卡在每台服务器上,一个是共有,另一个是私有的(heartbreak通信)*共享存储如SAN存储需要至少如下配置,并且需要连接到每台节点 ...
- 304 CORS
304响应, CORS问题: 没有 Access-Control-Allow-Origin 这个头信息时,以前次返回的200请求为准. 示例:可能已被删除 http://7af3zm.com1.z0. ...
- 代码研磨 Slim v3 (一)--app->get()&route->add()
index.php代码如下: $app->get('/forbase', function ($request, $response, $args){ Example\Module\Base:: ...
- SQL SERVER 中的行列转换小结
1. 介绍说明 前段时间组内的小伙伴在升级维护项目中,经常涉及一些复杂的数据转换问题,让我去看下有些地方怎么处理,我发现好多都是涉及到行列转换的问题,处理起来经常会比较麻烦,借此也总结一下,方便以后的 ...