自定义View之LayoutParams
来源:互联网 发布:铃声大全mac 编辑:程序博客网 时间:2024/06/05 09:07
前言
顾名思义,LayoutParams是一个用于保存和布局有关的属性的类,layout_width和layout_height两个必不可少的属性就是由它管理的,可见它的重要性。本文将详细介绍LayoutParams类,以及如何为自己的ViewGroup实现LayoutParams。
ViewGroup.LayoutParams
LayoutParams是ViewGroup的一个静态内部类,所有其他ViewGroup自定义的LayoutParams都是直接或间接地从它继承而来。首先看看它的几个域:
public static class LayoutParams {public static final int MATCH_PARENT = -1;public static final int WRAP_CONTENT = -2;public int width;public int height;}
想必不需要多解释。特别注意一下,MATCH_PARENT和WRAP_CONTENT两个标志位的值都是负的,这个特点下面会用到。
接下来看看它的几个构造器:
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();}protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { width = a.getLayoutDimension(widthAttr, "layout_width"); height = a.getLayoutDimension(heightAttr, "layout_height");}
利用TypedValue类在布局文件中提取出layout_width和layout_height两个属性,并保存在了width和height中。不了解TypedValue的话可以看自定义View之添加自定义属性。这里很重要的一点是,在保存时不会区分获得的是固定值(如100dp)还是标志位(如MATCH_PARENT)。为了在使用时能够区分,MATCH_PARENT和WRAP_CONTENT两个标志位的值都是负的。也就是说,如果width或者height的值≥0,那么就代表固定值,否则就代表一个标志位。这个特性在构建MeasureSpec时常常用到。
public LayoutParams(LayoutParams source) { this.width = source.width; this.height = source.height;}
根据一个现有的LayoutParams来设置自己的width与height域。
public LayoutParams(int width, int height) { this.width = width; this.height = height;}
根据给定的width与height设置相应的域,在动态创建view时常常会用到。
ViewGroup.MarginLayoutParams
相比于原生的ViewGroup.LayoutParams,自定义的LayoutParams往往会选择继承自ViewGroup.MarginLayoutParams,这个类在父类的基础之上提供了对margin属性的支持。margin属性是子view的位置相对于父view边界或是其他子view的偏移量,有leftMargin、topMargin、rightMargin、bottomMargin四种。另外,还有一个总的margin属性。在布局文件中,如果设置了margin属性,另外四个属性就会失效,实际的值会全部取和margin相等。
MarginLayoutParams的构造器无非是在LayoutParams的基础上增加了关于提取与设置各个margin属性的代码,这里就不重复贴出来了。
LayoutParams是如何发挥作用的
在向一个父view中添加新的子view时,偷懒的人往往会直接这么写:
addView(myView);
单从字面意思上看,这句代码仅仅提供了要添加的view的引用,完全没有任何关于这个view应当有多大,放在什么位置的信息。实际上,这些信息父view在背后帮你补上了。下面通过addView()方法的实现,来看看LayoutParams是如何发挥作用的。
首先是最简单的版本:
public void addView(View child) { addView(child, -1);}
没什么内容。继续看下去:
public void addView(View child, int index) { if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } //获取子view的LayoutParams LayoutParams params = child.getLayoutParams(); if (params == null) { //如果子view没有设置LayoutParams,就为它生成一个默认的 params = generateDefaultLayoutParams(); //默认生成的LayoutParams不能为null if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); } } addView(child, index, params);}protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);}
这里可以清楚地看到,父view会判断将要添加的子view有没有自带一个LayoutParams。如果没有的话,会调用generateDefaultLayoutParams()为它提供一个默认的。ViewGroup默认会提供一个width与height均为WRAP_CONTENT的LayoutParams。如果ViewGroup的子类实现了自己的LayoutParams,一般需要覆盖该方法。
public void addView(View child, int index, LayoutParams params) { if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } //重新进行measure与layout requestLayout(); //重绘界面 invalidate(true); addViewInner(child, index, params, false);}
添加新的子view之后自然需要重新执行measure、layout与draw。重点在addViewInner()方法:
private void addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout) { //省略大量代码... //确认子View的LayoutParams是否合法 if (!checkLayoutParams(params)) { //如果不合法,则提取这个params中有用的信息,并生成一个合法的LayoutParams params = generateLayoutParams(params); } //省略大量代码... }protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p != null;}protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return p;}
checkLayoutParams()与generateLayoutParams()方法一般也是需要重写的。可以看出,ViewGroup在确保子view的LayoutParams合法这一方面下了很大功夫。这也充分体现了LayoutParams的重要性。
最后补充一个方法:
public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs);}
这个方法在ViewGroup中没有直接调用到,它的作用是用xml中加载的属性生成一个LayoutParams对象。如果你想要给自己的LayoutParams添加自定义属性,就必须同时覆盖这个方法。例:
@Overridepublic ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new MyLayout.LayoutParams(getContext(),attrs);}
总结:如何为自己的ViewGroup实现LayoutParams
关于如何为自己的ViewGroup实现LayoutParams,前面部分已经讲的差不多了,只差一步——为LayoutParams提供自定义属性。这部分其实和为view提供自定义属性的方法是一样的:首先在attrs.xml中声明declare-styleable,然后在里面设置属性的名称与数据类型,最后在LayoutParams的构造器中提取。可以参考自定义View之添加自定义属性 。
最后将步骤重新整理一遍:
(1)attrs.xml中声明declare-styleable,并设置属性的名称与数据类型;
(2)创建LayoutParams的子类,并在其构造器中提取自定义属性;
(3)覆盖ViewGroup的generateDefaultLayoutParams()方法为子view提供默认LayoutParams实现
(4)覆盖ViewGroup的checkLayoutParams()验证子view的LayoutParams是否合法。
(5)覆盖两个重载版本的generateLayoutParams()方法。
- 自定义View之LayoutParams
- Android 自定义View总结 —— LayoutParams
- 自定义LayoutParams
- android自定义View探索5(onMeasure深入分析二LayoutParams)
- WindowManager.LayoutParams//android.view
- Android View.ViewGroup.LayoutParams
- 【View System】LayoutParams
- view/WindowManager.LayoutParams 下
- LayoutParams继承于Android.View.ViewGroup.LayoutParams.
- LayoutParams继承于Android.View.ViewGroup.LayoutParams.
- LayoutParams继承于Android.View.ViewGroup.LayoutParams.
- 自定义view之继承view
- 自定义View之 继承View
- Android 自定义View 之 自定义View属性
- 自定义View(一)之初识自定义View
- 自定义View之自定义标题栏
- 自定义View之自定义属性
- 自定义view,之自定义button
- MAC 使用pycharm出现ImportError: No module named numpy 解决方法
- Python 3 之数据类型
- dos命令之 set (显示、设置或删除 cmd.exe 环境变量) 用法详解以及使用变量
- Hbase 行健设计原则(待完善)
- 为什么TCP连接的建立是需要三次,而断开却需要四次
- 自定义View之LayoutParams
- 避免大规模故障的微服务架构
- string 中文乱码
- 形参 实参 用函数交换变量
- C语言__attribute__的使用
- Javascript全局变量的三种声明方式
- oracle怎么截取字符串中某个字符前(或者后)的字符串?
- app.json Expecting 'STRING','NUMBER','NULL','TRUE','FALSE','{','[',']', got INVALID
- 关于仿ios底部向上弹出dialog的最简单方法