Android如何自定义视图之测量和布局原理(一)
来源:互联网 发布:哈尔滨程序员工资 编辑:程序博客网 时间:2024/05/21 01:47
在android开发过程中,自定义视图的使用基本上可以说是非常频繁,如何开发一个高效的自定义视图,显得非常重要。由于android本身就是mvc的架构,假如视图的逻辑耦合到controller里,就会显得非主流,并且对View的控制显得不是得心应手,各种各样的适配问题也会接踵而来。这个时候View的作用就显现出来,View的逻辑还是要放到V这一层去控制。
那么如何实现高效有用的自定义View?
1、首先剥离View的功能,View只实现视图显示的功能,不能把业务逻辑掺杂进来,否则这个View又是四不像
2、View重写onMeasure,ViewGroup重写onMeasure、onLayout方法,实现视图的大小和位置的重新定义
3、对外提供相应的方法,实现对View的动态控制
第一点,抽离View的业务逻辑:就是分析需求,寻找View的功能,划清边界,不能“越权”执行代码,把View自己的功能内聚到自己的类里。
第二点,对于视图有需求变化宽高的情况,在onMeasure里实现视图宽高的测量,以及子视图的测量。在onLayout里变换视图的布局位置等。
第三点,对外暴漏通俗易懂的方法,让使用者使用方便,实现的过程中,尽量保证高的拓展性,对新需求能保证很快的拓展进来。
一起来看下onMeasure的具体原理:
先来看下onMeasure(int widthMeasureSpec, int heightMeasureSpec),参数的含义代表什么呢?我们来看下View的默认实现。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }setMeasuredDimension(int measureWidth,int measureHeight)是必须在onMeasure()测量完毕之后调用的,那么我们来看下getDefaultSize(int size, int measureSpec)的实现。
public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; }能看到switch..case条件判断的三种模式,通过查询api说明MeasureSpec是这样的:
一个MeasureSpec封装了从父容器传递给子容器的布局需求,每一个MeasureSpec代表了一个宽度,或者高度的说明。
一个MeasureSpec是一个大小跟模式的组合值。一共有三种模式:
1、 UPSPECIFIED:父容器对于子容器没有任何限制,子容器想要多大就多大.
2、 EXACTLY:父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间.
3、 AT_MOST:子容器可以是声明大小内的任意大小.
那么父类传过来的size是怎么组合到MeasureSpec中呢?一起看下MeasureSpec这个类:注释已经去掉,
private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; public static final int UNSPECIFIED = 0 << MODE_SHIFT; public static final int EXACTLY = 1 << MODE_SHIFT; public static final int AT_MOST = 2 << MODE_SHIFT;
public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); }MODE_MASK这个变量是将16进制3向左移位30,那么
MODE_MASK结果是这样的11000000000000000000000000000000;
~MODE_MASK是MODE_MASK的取反00111111111111111111111111111111;
UNSPECIFIED就是32位的00000000000000000000000000000000;
EXACTLY移位结果就是01000000000000000000000000000000;
AT_MOST移位结果就是10000000000000000000000000000000。
因为只有3中结果,2位就可以搞定,所以用3来作为mask。
getMode就获取了形参的最高两位,getSize就获取了形参低30位,也就是测量的实际dimension。
然后来看下onLayout:
onLayout是ViewGroup的抽象方法,方法的声明是这样的
protected void onLayout(boolean changed, int l, int t, int r, int b);参数:
changed代表View相对于父布局位置或者大小是否发生变化
l代表View相对于父布局左边的间距
t代表View相对于父布局顶部的间距
r代表View相对于父布局右侧的间距
b代表View相对于父布局底部的间距
当onLayout被系统调用时,需要去给每个子view分配一个大小和size。来看个onLayout的demo:
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = this.getChildCount(); for (int i = 0; i < count; i++) { View child = this.getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.layout(lp.x + lp.leftMargin, lp.y + lp.topMargin, lp.x + lp.leftMargin + child.getMeasuredWidth(), lp.y + lp.topMargin + child.getMeasuredHeight()); } }这个重载的方法是绝对按照子view的leftMargin和topMargin去约束子View的位置。
好了,简单的一起看了下onMeasure和onLayout的实现,以后一起跟大家对View进行更深入的解读,一起学习,如有问题欢迎指正,谢谢大家。
- Android如何自定义视图之测量和布局原理(一)
- android自定义控件系列教程----视图的测量和布局
- 自定义控件之视图篇(一)—— 测量与布局
- 自定义控件三部曲视图篇(一)——测量与布局
- Android自定义控件系列一:如何测量控件尺寸
- 自定义控件之测量篇:测量与布局
- Android之View的视图测量过程
- android自定义view之测量父view和迭代测量子view
- Android自定义控件(一)--View的测量
- 开源项目之Android ViewBadger(自定义的视图布局)
- Android自定义View(一) View的测量
- android 自定义View之View的测量(onMeasure()方法)
- Android 自定义View 之测量过程(onMeasure)
- android 自定义View之View的测量(onMeasure()方法)
- Android自定义视图动画(一)
- Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一)
- Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一)
- Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一)
- Memcached 客户端程序三种API的比较
- Navgationcontroller 的pop
- eclipse名字的由来及各版本的命名
- 沟通中的情绪管理(演讲稿)
- redhat下svn+apache搭建
- Android如何自定义视图之测量和布局原理(一)
- HTTP 错误 404.17 - Not Found 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理
- 一维邮局问题 + 带权中位数复杂度O(n)算法
- Java有关数组使用工具
- 关于x86、i386、i486、i586和i686等名词的解释
- LightOj 1112 Curious Robin Hood(线段树||树状数组)
- kettle在windows下执行job脚本 定时
- Android实现炫酷SVG动画效果
- 接口封装设计