百分比布局方式的适配?
屏幕适配无非就是适配各种机型上尺寸,尽可能让UI元素表现一致,这句话是没问题的,无非就是height、widhth和marginxx的尺寸,难道还有其他的吗?而百分比布局的适配就是,以父容器的尺寸作为参考,在View的加载过程中,根据父容器的实际尺寸换算出目标尺寸,然后作用于View,这句可能比较抽象,所以我们在手写百分比布局之前让我们来看看View的加载原理如何?就从setContentView开始吧。
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//创建DecorView和ContentView,并将ContentView添加到DecorView上,记住mContentParent就是DecorVIew的内容布局,就是开发者setContentView传进来的布局的父容器
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
//将要加载的布局资源添加到mContentParent上
mLayoutInflater.inflate(layoutResID, mContentParent);
}
为了简介所以省了很多代码,View的加载时从Activity的setContentView开始,然后在setContentView中调用mLayoutInflater.inflate(layoutResID, mContentParent);填充ContentView,所以我们一直跟踪最终到inflate的重载方法,代码如下。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
View result = root;
// temp就是xml中定义的根布局,而root就是DecorView的内容布局
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
// 创建与根布局匹配的布局参数(如果提供),contentView生成布局参数,并设置给child
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// 使用root提供的布局参数设置 child
temp.setLayoutParams(params);
}
}
// 这里的逻辑和上面的一样,上面的处理DecrView的contentView生成的LayoutParam做了处理,而这里则是开发者自己在xml中定义根View,逻辑也是根据根View生成LayoutParam提供根child。
rInflateChildren(parser, temp, attrs, true);
}
1、在inflate方法中主要是处理DecorView的contentView提供的LayoutParam,并将contentView生成的LayoutParam提供根child。
2、在rInflateChildren方法的逻辑和inflate方法的逻辑一样的,不过rInflateChildren方法是处理的开发者在xml中定义的布局,而且这个方法是个递归方法。
3、generateLayoutParams方法值ViewGroup的方法,用于生成LayoutParam的。
rInflateChildren方法:
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
rInflate方法
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
// 生成LayoutParam
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
// 递归调用
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
最后结论就是子View的LayoutParam是有父View提供的,所以我们知道了这个原理就好办了,我们可以通过自定义LayoutParam,并在父容器中修改LayoutParam就可以满足我们今天要讲内容了。
手写百分比布局
public class PercentLayout extends RelativeLayout {
public PercentLayout(Context context) {
super(context);
}
public PercentLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PercentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
ViewGroup.LayoutParams params = child.getLayoutParams();
//如果是百分比布局属性
if (checkLayoutParams(params)) {
LayoutParam lp = (LayoutParam) params;
float mLayoutHeightPercent = lp.mLayoutHeightPercent;
float mLayoutWidthPercent = lp.mLayoutWidthPercent;
float mLayoutMarginLeftPercent = lp.mLayoutMarginLeftPercent;
float mLayoutMarginRightPercent = lp.mLayoutMarginRightPercent;
float mLayoutMarginTopPercent = lp.mLayoutMarginTopPercent;
float mLayoutMarginBottomPercent = lp.mLayoutMarginBottomPercent;
if (mLayoutHeightPercent > 0) {
params.height = (int) (heightSize * mLayoutHeightPercent);
}
if (mLayoutWidthPercent > 0) {
params.width = (int) (widthSize * mLayoutWidthPercent);
}
if (mLayoutMarginLeftPercent > 0) {
((LayoutParam) params).leftMargin = (int) (widthSize * mLayoutMarginLeftPercent);
}
if (mLayoutMarginRightPercent > 0) {
((LayoutParam) params).rightMargin = (int) (widthSize * mLayoutMarginRightPercent);
}
if (mLayoutMarginTopPercent > 0) {
((LayoutParam) params).topMargin = (int) (heightSize * mLayoutMarginTopPercent);
}
if (mLayoutMarginTopPercent > 0) {
((LayoutParam) params).bottomMargin = (int) (heightSize * mLayoutMarginBottomPercent);
}
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParam(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParam;
}
// 通过查看setContentView的源码,得知,child的布局属性都是根据父容器创建的。
//为了保证还能够使用RelativeLayout的布局属性,所以需继承RelativeLayout.LayoutParams
public static class LayoutParam extends RelativeLayout.LayoutParams {
private float mLayoutHeightPercent;
private float mLayoutWidthPercent;
private float mLayoutMarginLeftPercent;
private float mLayoutMarginRightPercent;
private float mLayoutMarginTopPercent;
private float mLayoutMarginBottomPercent;
@SuppressLint("CustomViewStyleable")
public LayoutParam(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray typedArray = c.obtainStyledAttributes(attrs, R.styleable.MyPercentLayout);
mLayoutHeightPercent = typedArray.getFloat(R.styleable.MyPercentLayout_layout_heightPercent2, 0f);
mLayoutWidthPercent = typedArray.getFloat(R.styleable.MyPercentLayout_layout_widthPercent2, 0f);
mLayoutMarginLeftPercent = typedArray.getFloat(R.styleable.MyPercentLayout_layout_marginLeftPercent2, 0f);
mLayoutMarginRightPercent = typedArray.getFloat(R.styleable.MyPercentLayout_layout_marginRightPercent2, 0f);
mLayoutMarginTopPercent = typedArray.getFloat(R.styleable.MyPercentLayout_layout_marginTopPercent2, 0f);
mLayoutMarginBottomPercent = typedArray.getFloat(R.styleable.MyPercentLayout_layout_marginBottomPercent2, 0f);
typedArray.recycle();
}
}
}
代码很简单,我们通过自定义LayoutParam并继承了RelativeLayout.LayoutParams,主要是想保留RelativeLayout.LayoutParams的布局属性,就我们就是做了个拓展,然后我们还重写了generateLayoutParams方法,并在此方法中返回我们的自定义的LayoutParam,并在onMeasure中对所有child进行遍历修改child的LayoutParam,得以满足做到百分比适配方法,虽说百分比适配方法是已经过时了,它的缺点在于不能精确的做到是适配,比如我想这个Button距离左边20dp,那百分比就做不到,百分比就是通过父容器的width和height然后剩余指定的百分比,并不能作为任何场景的适配。
网友评论