Android自定义View
来源:互联网 发布:软件行业沙龙 编辑:程序博客网 时间:2024/05/16 01:28
view的显示过程:
ActivityThread中,activity对象被创建后,在activity的onresume方法中会创建ViewRoot对象将DecorView添加到window中(控制DecorView的外观和行为策略,有唯一的实现类PhoneView),ViewRoot对象的performTraversal方法完成顶级View(decorview)的mesure,layout,draw方法,measure方法会调用onMeasure方法,onMeasure方法会对所有子元素进行measure过程
创建自定义View
- 1.自定义View的属性
attrs:我们要获取的属性的资源ID的一个数组,就是从一堆属性中我们希望查询什么属性的值,AttributeSet可以获得布局文件中定义的所有属性(attrs:定义属性类型;styles:定义属性值)
如key和value:attrName = layout_width , attrVal = @2131165234
TypedArray:其实是用来简化我们的工作的,比如上例,如果布局中的属性的值是引用类型(比如:@dimen/dp100),如果使用AttributeSet去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。
declare-styleable:
attr不依赖于styleable,styleable只是为了方便attr的使用。
我们自己定义属性完全可以不放到styleable里面,比如直接在resources文件中定义一些属性:
attr name=”custom_attr1” format=”string”/>
attr name=”custom_attr2” format=”string”/>
定义一个attr就会在R文件里面生成一个Id,那么我们去获取这个属性时,必须调用如下代码:
int[] custom_attrs = {R.attr.custom_attr1,R.custom_attr2};
TypedArray typedArray = context.obtainStyledAttributes(set,custom_attrs);
而通过定义一个styleable,我们可以在R文件里自动生成一个int[],数组里面的int就是定义在styleable里面的attr的id。所以我们在获取属性的时候就可以直接使用styleable数组来获取一系列的属性。
declare-styleable name=”custom_attrs”>
attr name=”custom_attr1” format=”string” />
attr name=”custom_attr2” format=”string” />
declare-styleable/>
获取:
TypedArray typedArray = context.obtainStyledAttributes(set,R.styleable.custom_attrs); - 2.从View的构造方法中获得我们自定义的属性
- 3.重写onMeasure,onLayout,onDraw方法
onMeasure():
- MeasureSpec
代表一个32位int值,高2位是SpecMode,低30位是SpecSize(mode和size也分别都是int值);SpecMode分为exactly,at_most,unspecified(父容器和view)
-LayoutParams
LayoutParams类是用于child view(子视图) 向 parent view(父视图)传达自己的意愿尺寸的一个东西。分为LayoutParams.MTACH_PARENT和LayoutParams.WRAP_CONTENT和固定大小。match和固定大小对应于上面的exactly,wrap对应于上面的at_most
DecorView的MeasureSpec由自身的LayoutParams和窗口尺寸确定的;普通view由自身的LayoutParams和父容器的MeasureSpec决定的;MeasureSpec确定后,onMeasure中就可以确定View的测量宽和高,view的最终宽高是layout阶段确定的,二者一般是一样的;
在layout中确定最终宽和高,
mLeft = left; mTop = top; mRight = right; mBottom = bottom; public final int getWidth() { return mRight - mLeft; } public final int getHeight() { return mBottom - mTop; }
但如果我们重写view的layout方法:
public void layout(int l, int t, int r, int b) { super.layout(l, t, r + 100, b + 100); }
这样会使最终宽和高比测量的大100
view的measure方法会调用onMeasure方法,默认会调用getDefaultSize()方法来获取视图的大小,setMeasuredDimension()方法来设定测量出的大小;
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
接下来我们来看 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)源码
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; }
首先会判断specMode,如果是AT_MOST和EXACTLY就是view测量后的大小,UNSPECIFIED我们要看getSuggestedMinimumHeight()源码
protected int getSuggestedMinimumHeight() { return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight()); }
UNSPECIFIED是用于系统内部的测量过程,这个取决于view是否设置背景
ViewGroup除了完成自己的measure过程以外,还会遍历调用子元素的measure方法。ViewGroup是一个抽象类,不同实现子类的布局特性不同,导致测量细节也不同,onMeasure方法需要各个子类的具体实现
eg:linearlayout的onMeasure方法
垂直布局的linearlayout横向上遵循view的测量过程,纵向上若布局高度采用的是match_parent和具体数值那么测量过程也是和view一致,即是specSize,如果布局是wrap_content那么它的高度是所有子元素占用高度的总和加上竖直方向的padding
view的measure和activity的生命周期不是同步的,我们可以通过下列方式获得宽和高:
-Activity/View的onWindowFocusChanged(此时View已经初始化完毕)
-view.post(runnable),把runnable放入消息队列中然后等looper调用runnable的时候,view也已经初始化好了
onLayout():
layout的作用是ViewGroup用来确定子元素的位置,layout方法确定view本身的位置,onLayout确定子元素的位置,viewgroup在onlayout中遍历所有的子元素并调用layout方法,子元素又会调用onlayout,确定所有子元素的位置,onLayout方法需要具体实现(View和ViewGroup均没有具体实现),和实际布局相关。
Darw过程:
draw方法会遍历所有子元素的draw方法,一层层传递下去。
-绘制背景
-绘制自己
-绘制children
-绘制装饰
- 4.视图状态:enabled(是否可用),slelected(是否选中),pressed(是否按下),foucsed(是否获得焦点)
- 5.调用视图的setVisibility()、setEnabled()、setSelected()等方法时都会通过调用invalidate()方法来导致视图重绘
补充:自定义的view中,我们需要对warp_content和padding做特殊处理,分别在onMeasure()和onDraw()中处理warp_content和padding
自定义View的wrap_content和padding的处理
-在onMeasure()中处理wrap_content,设置一个具体的宽和高,并在wrap_content时设置成此宽和高
-在onDraw()中处理padding
- Android View---自定义View
- Android View---自定义View
- Android 自定义View 之 自定义View属性
- 【自定义View系列】android自定义View概述
- Android自定义view自定义属性
- Android自定义控件 -- 自定义View
- android自定义view(自定义数字键盘)
- Android自定义View-自定义属性
- Android自定义View-自定义属性
- Android 自定义View
- Android 自定义 View
- android自定义View
- Android 中自定义 view
- android 自定义view组件
- Android 自定义 View
- android 自定义view
- Android:如何自定义View
- android 自定义View
- MongoDB数据库设计中6条重要的经验法则,part 2
- SDWebImage使用方法
- 用因果图分析微博经验值规则生成测试用例
- 第十/十一周训练1-4
- 第十一周 阅读程序(5e)
- Android自定义View
- 将Cent0S 7的网卡名称eno16777736改为eth0
- POJ 3581 Sequence(后缀数组)
- 如何使用Eclipse上传一个Java web项目到Git@OSC上
- 第十/十一周训练1-5
- MongoDB数据库设计中6条重要的经验法则,part 3
- C++ Primer 学习笔记
- CSS设置行内元素和块级元素的水平居中、垂直居中
- 第十/十一周训练1-6