安卓的布局有很多种,每种都有对应的LayoutParams类,那么它们之间到底是什么关系?

为什么在编写Layout的XML文件时,有的layout_前缀属性有用有的没有用?

一句话道出LayoutParams的本质:LayoutParams是Layout提供给其中的Children使用的。我们来看一段不用XML文件创建布局的代码。

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL); TextView tv = new TextView(this);
tv.setText("I am a text view, haha");
tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
ll.addView(tv); setContentView(ll);
}

可见LayoutParams的对象是设置在Layout中的Child View上的,而不是设置在自己上。就是说View想要放到某种Layout中,就必须设置这个Layout规定的LayoutParams。

每一种Layout实现不同的布局方式都有其独特的参数设置,例如LinearLayout.LayoutParams的weight属性,用来设置Child View之间的大小比例,如果把这个设置给FrameLayout是没有意义的,因为FrameLayout并不会排列Child View而是会都“挤在一起”。所以在XML文件里设置View的属性的时候,有的layout属性是无效的,也是没有意义的。

如果仔细看上面那段代码,会发现有个问题,设置TextView的LayoutParams是ViewGroup.LayoutParams,而不是LinearLayout.LayoutParams。查看源代码可以看到LinearLayout.LayoutParams是ViewGroup.LayoutParams的子类,那么会不会产生运行时错误?下面通过分析源代码找到这个问题的答案。

使Layout与View发生关系的语句是LinearLayout.addView(),先找到LinearLayout.java(frameworks/base/core/java/android/widget/LinearLayout.java)发现没有override这个方法,到父类ViewGroup(frameworks/base/core/java/android/view/ViewGroup.java)中寻找addView。

    public void addView(View child) {
addView(child, -1);
}
public void addView(View child, int index) {
LayoutParams params = child.getLayoutParams();
if (params == null) {
// ......
}
addView(child, index, params);
}
public void addView(View child, int index, LayoutParams params) {
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}

addView有N个重载(overload)的版本,注意第二个是直接获取Child View的LayoutParams,这里没有经过处理,最后传递给了addViewInner()方法,再来看addViewInner()方法的代码:

    private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) { // ... if (!checkLayoutParams(params)) {
params = generateLayoutParams(params);
} // ...
}

发现里面有这样两个调用checkLayoutParams()和generateLayoutParams()可能会修改或替换传进来的params,找到这两个函数发现ViewGroup中的实现几乎是空的:

    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p != null;
}
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return p;
}

让我们回到LinearLayout里看看有没有override这两个方法:

    @Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LinearLayout.LayoutParams;
}
@Override
public LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LinearLayout.LayoutParams(p);
}

checkLayoutParams做的事情是检查这个LayoutParam对象是否是LinearLayout.LayoutParams的实例,按照刚开始写的代码运行到这里就返回false了,于是执行generateLayoutParams,通过ViewGroup.LAyoutParams创建一个新的LinearLayout.LayoutParams。这样就保证了使用的一定是LinearLayout.LayoutParams或者其子类。再看一下LinearLayout.LayoutParams的构造函数:

        public float weight;
public int gravity = -1;
public LayoutParams(ViewGroup.LayoutParams p) {
super(p);
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
}
public LayoutParams(LayoutParams source) {
super(source); this.weight = source.weight;
this.gravity = source.gravity;
}

显然应该调用的是第一个构造函数,然后weight和gravity的值取默认值。为什么要定义第一个和第二个构造函数?因为LinearLayout.LayoutParams继承自ViewGroup.MarginLayoutParams,而ViewGroup.MarginLayoutParams继承自ViewGroup.LayoutParams。可见二者是LinearLayout.LayoutParams继承树上的所有祖先类。添加这两个构造方法使能通过祖先类对象构造自己,配合上面的check和generate使不适配的LayoutParams变得可用。

根据以上分析,可以得出一个小结论,使用代码构建界面的时候,可以随意使用ViewGroup.LayoutParams或者ViewGroup.MarginLayoutParams,因为所有的Layout中的LayoutParams都继承自他们,但是也会付出一点点代价就是会新创建一个对象。不过毕竟靠代码构建界面的场景很少,一般用XML就可以了,这也算是个冷知识点吧。

安卓冷知识:LayoutParams的更多相关文章

  1. 前端不为人知的一面--前端冷知识集锦 前端已经被玩儿坏了!像console.log()可以向控制台输出图片

    前端已经被玩儿坏了!像console.log()可以向控制台输出图片等炫酷的玩意已经不是什么新闻了,像用||操作符给变量赋默认值也是人尽皆知的旧闻了,今天看到Quora上一个帖子,瞬间又GET了好多前 ...

  2. 前端不为人知的一面–前端冷知识集锦 原文地址(http://web.jobbole.com/83473/);

    前端已经被玩儿坏了!像console.log()可以向控制台输出图片等炫酷的玩意已经不是什么新闻了,像用||操作符给变量赋默认值也是人尽皆知的旧闻了,今天看到Quora上一个帖子,瞬间又GET了好多前 ...

  3. .Net冷知识之动态查找类型时的程序集路径问题

    今天就说说.Net中通过反射取得某个类型时,我们怎么知道这个类型在硬盘上的哪个角落?比如说,假如我们需要要求服务端动态载入某个数据源,那服务端怎么知道数据源在哪? 网上大部分的教程都写着,可以使用As ...

  4. 转:前端冷知识(~~some fun , some useful)

    前端不为人知的一面——前端冷知识集锦 前端已经被玩儿坏了!像console.log()可以向控制台输出图片等炫酷的玩意已经不是什么新闻了,像用||操作符给变量赋默认值也是人尽皆知的旧闻了,今天看到Qu ...

  5. 盘点 Python 中的那些冷知识(二)

    上一篇文章分享了 Python中的那些冷知识,地址在这里 盘点 Python 中的那些冷知识(一) 今天将接着分享!! 06. 默认参数最好不为可变对象 函数的参数分三种 可变参数 默认参数 关键字参 ...

  6. web 前端冷知识

    前端已经被玩儿坏了!像console.log()可以向控制台输出图片等炫酷的玩意已经不是什么新闻了,像用||操作符给变量赋默认值也是人尽皆知的旧闻了,今天看到Quora上一个帖子,瞬间又GET了好多前 ...

  7. UI设计师不可不知的安卓屏幕知识-安卓100分享

    http://www.android100.org/html/201505/24/149342.html UI设计师不可不知的安卓屏幕知识-安卓100分享 不少设计师和工程师都被安卓设备纷繁的屏幕搞得 ...

  8. python 冷知识(装13 指南)

    python 冷知识(装13 指南) list1 += list2 和 list1 = list1 + list2 的区别 alpha = [1, 2, 3] beta = alpha # alpha ...

  9. Python 浮点数的冷知识

    本周的PyCoder's Weekly 上分享了一篇小文章,它里面提到的冷知识很有意思,我稍作补充,分享给大家. 它提到的部分问题,读者们可以先思考下: 若两个元组相等,即 a==b 且 a is b ...

随机推荐

  1. SVN代码回滚命令之---"svn up ./ -r 版本号"---OK

    一.改动还没被提交的情况(未commit) 这种情况下,见有的人的做法是删除work copy中文件,然后重新update,恩,这种做法达到了目的,但不优雅,因为这种事没必要麻烦服务端. 其实一个命令 ...

  2. Socket Server-基于NIO的TCP服务器

    NIO主要原理及使用 NIO采取通道(Channel)和缓冲区(Buffer)来传输和保存数据,它是非阻塞式的I/O,即在等待连接.读写数据(这些都是在一线程以客户端的程序中会阻塞线程的操作)的时候, ...

  3. Spring 事务注解 错误问题

    碰到个问题,在一个springmvc项目中,设置好事务,然后在service上添加@Transactional注解,只要一添加这个注解,该service就无法被spring创建成功, error cr ...

  4. HDU 1698 Just a Hook(线段树 区间替换)

    Just a Hook [题目链接]Just a Hook [题目类型]线段树 区间替换 &题解: 线段树 区间替换 和区间求和 模板题 只不过不需要查询 题里只问了全部区间的和,所以seg[ ...

  5. Bugtags奉命解救宝贵的双手,务必将此文章转给你身边的程序猿

    移动应用 Bug 快速反馈神器 前段时间,有很多 APP 突然走红,最终却都是樱花一现.作为一个创业团队,突然爆红是非常难得的机会.但是很可惜,由于没有经过充分的测试,再加上用户的激增,APP 闪退. ...

  6. java byte&0xFF

    做串口端口通讯时,数据都是以byte类型发送的 普通的byte范围是-128-127,而java的byte范围是0-255 因此将数据的byte转成java的byte时,需要与0xff(1111111 ...

  7. gradle修改AndroidManifest.xml中的版本号

    def VersionCode = "19" ant.replaceregexp(file:"../Assets/Plugins/Android/AndroidManif ...

  8. [hihoCoder1329] 带Split和Merge的Treap

    题目链接:http://hihocoder.com/problemset/problem/1329 这题本来是学Splay的题,但是我为了练习Treap的Split和Merge操作,就借来用一用. 就 ...

  9. php-建造者模式(Builder)解析

    其与抽象模式相类似,都可以创建复杂的对象,但是抽象工厂更注重多个系列的产品对象,而Builder模式则着重于一步一步的构建一个复杂的对象,在最后一步才返回产品, 使用建造者模式的好处是: 1.将构造代 ...

  10. 安卓通用shell大全

    一.[什么是shell] Linux系统的shell作为操作系统的外壳,为用户提供使用操作系统的接口.它是命令语言.命令解释程序及程序设计语言的统称.shell是用户和Linux内核之间的接口程序,如 ...