Android 百分比规划库(percent-support-lib) 解析与拓展-Android-优质IT资源分享社区

admin
管理员
管理员
  • UID1
  • 粉丝27
  • 关注4
  • 发帖数581
  • 社区居民
  • 忠实会员
  • 原创写手
阅读:197回复:0

  Android 百分比规划库(percent-support-lib) 解析与拓展

楼主#
更多 发布于:2016-05-20 19:01

一、概述
周末游戏打得过猛,于是周天熬夜码代码,周一早上浑浑噩噩的发现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。咱们就简略过一下:
首要记住在build.gradle增加:
compile 'com.android.support:percent:22.2.0'
(一)PercentFrameLayout

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

(二) PercentRelativeLayout

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

运用没什么好说的,即是直观的看一下。
三、源码剖析
本来细想一下,Google只是对咱们本来了解的RelativeLayout和FrameLayout进行的功用的拓展,使其支撑了percent有关的特点。
那么,咱们思考下,假如是咱们增加这种拓展,咱们会怎么做:
经过LayoutParams获取child设置的percent有关特点的值
onMeasure的时分,将child的width,height的值,经过获取的自定义特点的值进行核算(eg:容器的宽
* fraction ),核算后传入给child.measure(w,h);
ok,有了上面的猜测,咱们直接看PercentFrameLayout的源码。
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接口。
这个接口很简略,只要一个办法:
public interface PercentLayoutParams {      
 PercentLayoutInfo getPercentLayoutInfo();}
而,这个办法的完结呢,也只要一行:return
mPercentLayoutInfo;,那么这个mPercentLayoutInfo在哪完结赋值呢?
看PercentFrameLayout.LayoutParams的结构办法:
public LayoutParams(Context c, AttributeSet attrs)
{       super(c, attrs);       mPercentLayoutInfo =
PercentLayoutHelper.getPercentLayoutInfo(c, attrs);}
能够看到,将attrs传入给getPercentLayoutInfo办法,那么不用说,这个办法的内部,必定是获取自定义特点的值,然后将其封装到PercentLayoutInfo目标中,终究回来。
代码如下:
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的尺度
@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办法。
/**     * 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也是相似的)。
/**         * Fills {@code 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办法:
@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过小的话,会进行一些操作。
代码如下:
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判别:
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里边干什么毛线:
@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
/**     * 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。康复很简略:
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
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);        }    }}
假如你具体看了上面的源码剖析,这个代码是不是没撒解说的了~
(二)测验规划

咱们纵向摆放的几个TextView,别离设置宽/高都为百分比,且之间的距离为5%p。
(三)效果图

ok,到此,咱们运用、源码剖析、拓展PercentLinearLayout就完毕了。
增加PercentLinearLayout后的地址:点击检查
拓展下载:android-percent-support-extend 包括android
studio, eclipse项目,以及上述源码。








优质IT资源分享社区为你提供此文。
本站有大量优质android教程视频,资料等资源,包含android基础教程,高级进阶教程等等,教程视频资源涵盖传智播客,极客学院,达内,北大青鸟,猎豹网校等等IT职业培训机构的培训教学视频,价值巨大。欢迎点击下方链接查看。

android教程视频
优质IT资源分享社区(www.itziyuan.top)
一个免费,自由,开放,共享,平等,互助的优质IT资源分享网站。
专注免费分享各大IT培训机构最新培训教学视频,为你的IT学习助力!

!!!回帖受限制请看点击这里!!!
!!!资源失效请在此版块发帖说明!!!

[PS:按 CTRL+D收藏本站网址~]

——“优质IT资源分享社区”管理员专用签名~

本版相似帖子

游客