您的位置:首页 > 移动开发 > Android开发

《Android群英传》读书笔记(1)第三章:Android控件与自定义控件详解

2015-12-14 12:11 357 查看

一、Android控件架构

1.在Android中控件大致分为两类:ViewGroup和View,ViewGroup作为父控件可以包含多个View控件,这就是我们常说的控件树。上层控件负责下层控件的测量和绘制,并传递交互事件。

2.Android界面架构图:每个Activity都包含一个Window对象,多数由PhoneWindow来实现。PhoneWindow将一个DecorView设置为整个应用窗口的根View。DecorView作为窗口界面的顶层视图,封装了一些操作窗口的通用方法。这里所有的View监听事件都通过WindowManagerService来进行接收。DecorView将屏幕分成两部分,一个是TitleView,一个是ContentView,ContentView是一个id为content的FrameLayout,activity_main.xml就是设置在这样一个FrameLayout里。requestWindowFeature()需要在setContentView()之前调用才能生效。当程序运行到setContentView()的方法后,ActivityManagerService会回调onResume()方法,此时系统才会把整个DecorView添加到PhoneWindow中。



3.View的测量:View的测量在onMeasure()方法中进行,MeasureSpec类来帮助我们测量View,MeasureSpec是一个32位的int值,其中高2位为测量模式,低30位为测量的大小,在计算中使用位运算可以提高并优化效率。

测量模式分为三种:

①EXACTLY(精确模式),当我们指定layout_width或layout_height为具体数值时或指定为match_parent时,使用的是该模式。

②AT_MOST(最大值模式),当我们指定layout_width或layout_height为wrap_content时,控件大小一般随着自控件或者控件的内容而变化,此时控件的尺寸只要不超过父控件的最大尺寸即可。

③UNSPECIFIED,它不指定其大小测量模式,通常在绘制自定义view时使用。

View类默认的onMeasure()方法只支持EXACTLY模式,自定义控件时需要重写该方法来支持其他模式。

onMeasure()方法实现举例:

@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
int measuredWidth = measureWidth(widthMeasureSpec);
int measuredHeight = measureHeight(heightMeasureSpec);
setMeasureDimension(measuredWidth,measuredHeight);
}

private int measureWidth(int widthMeasureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if(specMode == MeasureSpec.EXACTLY){
result = specSize ;
}else {
//否则为AT_MOST或UNSPECIFIED模式
result = 100;
if(specMode == MeasureSpec.AT_MOST){
//具体情况要根据控件内容或子控件大小测量view
result = Math.min(specSize ,result);
}
}
return result;
}

private int measureHeight(int heightMeasureSpec){
//和measureHeight()实现类似
...
}


4.view的绘制:view的绘制在onDraw(Canvas canvas)中进行,onDraw有一个参数canvas,即画布。当创建一个Canvas时:
Canvas mCanvas = new Canvas(bitmap);
需要传入一个bitmap对象,以后通过mCanvas绘制的内容都在bitmap上,这叫做装载画布。

5.ViewGroup的测量和绘制

ViewGroup的大小设置为wrap_content时,ViewGroup会遍历其子view,通过调用子view的Measure方法,获得所有的子view大小,来决定自己的大小。而在其他模式下则会根据具体的数值设定自己的大小。

当子view测量完毕就需要将子view放到合适的位置,这个过程就是view的layout过程。ViewGroup在执行layout时,同样是使用遍历来执行子view的layout方法,并制定其具体显示位置。

如果自定义ViewGroup,通常会去重写onLayout()方法来控制子view显示位置的逻辑。同样,如果需要支持wrap_content属性,还需要重写onMeasure()方法。这点与View是相同的。

ViewGroup通常是不需要绘制的,除非指定了其背景颜色,因此ViewGroup的onDraw方法都不会被调用。但是它会使用dispatchDraw()方法来绘制其字view,同样是遍历所有字view并调用子view的绘制方法来完成绘制工作。

6.自定义View

几个重要的回调方法:

onFinishInflate():从XML加载组件后回调。

onSizeChanged():组件大小改变时回调。

onMeasure():回调该方法来进行测量。

onLayout():回调该方法来确定显示的位置。

onTouchEvent():监听到触摸事件时的回调。

自定义view的三种方法:

对现有的控件进行拓展。

通过组合来实现新的控件。

重写view来实现全新的控件。

控件的宽高在onSizeChanged里获取。

使用 标签在values/attrs.xml中自定义控件属性,在java中通过TypedArray来获得自定义的属性,并记得使用完后要调用ta.recycle()来进行回收。

最好先定义属性,然后再在中进行引用,像这样:

<? xml version = "1.0" encoding = "utf-8" ?>
< resources >
< attr name = "icon" format = "integer" />
< declare-styleable name= "PreferenceHeader" >
<!-- Identifier value for the header. -->
< attr name= "id" format = "integer"/>
< attr name= "icon" />
<!-- The fragment that is displayed when the user selects this item. -->
</declare-styleable >
< declare-styleable name= "Preference" >
< attr name= "icon"  />
<!-- The key to store the Preference value. -->
< attr name= "key" format = "string" />
</declare-styleable >
</ resources >


否则在编译时可能会遇到Attribute ** has already been defined的错误。或者更改冲突的属性名。

7.自定义ViewGroup

ViewGroup的存在目的就是为了管理子view,为其子view添加显示、响应的规则。因此自定义ViewGroup需要重写onMeasure()对子view进行测量,重写onLayout()确定子view的位置,重写onTouchEvent()增加响应时间。

方法getScrollY()、getScrollX()返回的是ViewGroup内容滑出屏幕的部分距离屏幕边缘的距离。例如ViewGroup中的内容向上移动了100像素,则getScrollY() = 100;即是移除去的部分的top距离屏幕上边缘的长度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: