一、概述
周末游戏打得过猛,于是周天熬夜码代码,周一早上浑浑噩噩的发现 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 |
|
(一)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 |
|
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 |
|
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 |
|
代码是相当的短,可以看到 PercentFrameLayout 里面首先重写了generateLayoutParams方法,当然了,由于支持了一些新的layout_属性,那么肯定需要定义对应的LayoutParams。
(一)percent相关属性的获取
可以看到PercentFrameLayout.LayoutParams在原有的FrameLayout.LayoutParams基础上,实现了PercentLayoutHelper.PercentLayoutParams接口。
这个接口很简单,只有一个方法:
[Java] 纯文本查看 复制代码
1 2 3 |
|
而,这个方法的实现呢,也只有一行: return mPercentLayoutInfo; ,那么这个mPercentLayoutInfo在哪完成赋值呢?
看PercentFrameLayout.LayoutParams的构造方法:
[Java] 纯文本查看 复制代码
1 2 3 4 |
|
可以看到,将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 |
|
是不是和我们平时的取值很类似,所有的值最终封装到PercentLayoutInfo对象中。
ok,到此我们的属性获取就介绍完成,有了这些属性,是不是onMeasure里面要进行使用呢?
(二) onMeasue中重新计算child的尺寸
[Java] 纯文本查看 复制代码
1 2 3 4 5 6 7 8 |
|
可以看到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 |
|
通过注释也能看出,此方法中遍历所有的孩子,通过百分比的属性重新设置其宽度和高度。
首先在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 |
|
首先保存原本的width和height,然后重置params的width和height为 (int) (widthHint * widthPercent) 和 (int) (heightHint * heightPercent);。
到此,其实我们的百分比转换就结束了,理论上就已经实现了对于百分比的支持,不过Google还考虑了一些细节。
我们回到onMeasure方法:
[Java] 纯文本查看 复制代码
1 2 3 4 5 6 7 8 |
|
下面还有个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 |
|
首先遍历所有的孩子,拿出孩子的layoutparams,如果是PercentLayoutParams实例,则取出info。如果info不为null,调用 shouldHandleMeasuredWidthTooSmall 判断:
[Java] 纯文本查看 复制代码
1 2 3 4 5 |
|
这
里就是判断,如果你设置的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 |
|
继续看 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 |
|
噗,原来是重新恢复原本的尺寸值,也就是说onMeasure里面的对值进行了改变,测量完成后。在这个地方,将值又恢复成如果布局文件中的值,上面写的都是0。恢复很简单:
1 2 3 4 |
|
你应该没有忘在哪存的把~忘了的话,麻烦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 |
|
如果你详细看了上面的源码分析,这个代码是不是没撒解释的了~
(二)测试布局
[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 |
|
我们纵向排列的几个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