前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ViewGroup的LayoutParams理解[通俗易懂]

ViewGroup的LayoutParams理解[通俗易懂]

作者头像
全栈程序员站长
发布2022-09-29 10:37:26
3520
发布2022-09-29 10:37:26
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君

LayoutParams是ViewGroup的一个内部类,声明方式如下

代码语言:javascript
复制
public static class LayoutParams { 

public static final int MATCH_PARENT = -1;
public static final int WRAP_CONTENT = -2;
public int width;
public int height;
public LayoutParams(Context c, AttributeSet attrs) { 

TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
setBaseAttributes(a,R.styleable.ViewGroup_Layout_layout_width, R.styleable.ViewGroup_Layout_layout_height);
a.recycle();
}
public LayoutParams(int width, int height) { 

this.width = width;
this.height = height;
}
public LayoutParams(LayoutParams source) { 

this.width = source.width;
this.height = source.height;
}
}

可以分析到,系统默认为ViewGroup自定义了宽高属性width和height,将其获取方式封装在LayoutParams中,系统考虑所有的View肯定都有宽高,所以就直接统一定义了,有一个疑问为啥不定义在View中,因为子View定义的宽高都是layout_width,layout_height,都是相对于父容器的 接下来分析,系统是如何将这个统一的ViewGroup.LayoutParams宽高属性给到View的呢?

阅读源码布局中View的加载流程,主要分析LayoutInflater#inflate这一步

代码语言:javascript
复制
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { 

final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) { 

params = root.generateLayoutParams(attrs);
if (!attachToRoot) { 

temp.setLayoutParams(params);
}
}                    
}

分析关键代码,是root(ViewGroup)调用了generateLayoutParams(AttributeSet attrs);解析attrs中的layout_width和layout_height属性,将其值封装到了LayoutParams中,之后调用子View的setLayoutParams方法设置到子View中,之后子View就可以获取到width和height了

所以默认情况下父容器的generateLayoutParams只是解析layout_width和layout_height。如果子view需要支持margin属性,不重写父容器的generateLayoutParams的话,之后是子View是获取不到margin属性的

再分析下ViewGroup#generateDefaultLayoutParams,在上面我们分析了ViewGroup#generateLayoutParams它是作用在xml布局解析阶段为子View设置LayoutParams,那么ViewGroup#generateDefaultLayoutParams则是在代码中ViewGroup#addView中调用,我们看下源码

代码语言:javascript
复制
public void addView(View child, int index) { 

if (child == null) { 

throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
}
LayoutParams params = child.getLayoutParams();
if (params == null) { 

params = generateDefaultLayoutParams();
if (params == null) { 

throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
}

分析源码得知,generateDefaultLayoutParams的调用确实在动态addView中,同样的它也是默认创建的ViewGroup.LayoutParams,只支持width和height的

再分析下ViewGroup#generateLayoutParams(LayoutParams p),分析ViewGroup#addViewInner源码

代码语言:javascript
复制
private void addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout) { 

if (!checkLayoutParams(params)) { 

params = generateLayoutParams(params);
}
}

分析可以知道,在addViewInner中会对params进行校验,如果不合法则调用generateLayoutParams(params)帮我们生成一个合法的LayoutParams.我们看下校验规则

代码语言:javascript
复制
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 

return  p != null;
}

ViewGroup默认的校验规则仅仅是不为空,相关宽松,LinearLayout等一般都会重写,有兴趣可以看源码

再分析下ViewGroup默认的generateLayoutParams(ViewGroup.LayoutParams p)实现

代码语言:javascript
复制
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 

return p;
}

仅仅是返回了这个不合法的params,这并不是我们期望的,所以这个用法一般是我们重写checkLayoutParams实现合法的校验规则,然后在重写generateLayoutParams创建合法的LayoutParams,看下LinearLayout的实现

代码语言:javascript
复制
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 

return p instanceof LinearLayout.LayoutParams;
}
代码语言:javascript
复制
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { 

if (sPreserveMarginParamsInLayoutParamConversion) { 

if (lp instanceof LayoutParams) { 

return new LayoutParams((LayoutParams) lp);
} else if (lp instanceof MarginLayoutParams) { 

return new LayoutParams((MarginLayoutParams) lp);
}
}
return new LayoutParams(lp);
}

前面分析了ViewGroup默认的LayoutParams只是支持layout_width和layout_height属性的,因为它内部只有获取这两个属性的逻辑,但是我们实际很多子View都是需要支持margin属性的,怎么办呢?

ViewGroup还为我们定义了一个MarginLayoutParams,是不是相当的便利

代码语言:javascript
复制
public static class MarginLayoutParams extends ViewGroup.LayoutParams { 

public int leftMargin;
public int topMargin;
public int rightMargin;
public int bottomMargin;
public MarginLayoutParams(Context c, AttributeSet attrs) { 

super();
...//省略若干获取margin属性操作代码
}
}

所以如果我们需要子View支持margin属性,那么我们就可以重写ViewGroup#generateLayoutParams(AttributeSet attrs)和ViewGroup#generateDefaultLayoutParams()返回MarginLayoutParams

问题来了,我如果还想让子View支持更多的属性呢?那么我们可以自定义LayoutParams继承自MarginLayoutParams,在构造方法中书写获取我们自定义属性的逻辑,同样重写上面两个方法,返回我们自定义的LayoutParams,这样子View就可以获取到了

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/193328.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年9月15日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档