参考:
自定义视图(View
)是 Android
开发的一个进阶内容。随着开发的深入,肯定会出现系统提供的基础控件不符合需求的情况。一方面通过组合基础控件以形成新的布局,另一方面可以通过自定义控件的方式来更加灵活的实现需求
自定义视图涉及到 Android
系统许多方面的内容,下面根据自己的理解顺序来讲一讲如何自定义视图
主要内容
视图层次结构浅析
View
和 ViewGroup
是所有组件的基类
View
的作用是在屏幕上绘制可供用户交互的内容
ViewGroup
的作用是储存其它 View
或者 ViewGroup
,作为定义界面的布局。
通俗的说,View
是所有组件的基类,ViewGroup
是布局组件(LinearLayout
/ RelativeLayout
等)的基类
视图层次结构图如下:
Note:自定义视图,必须继承 View
或者 View
的子类;如果想要自定义布局,必须继承 ViewGroup
或者其子类
下面先叙述如何继承 View
实现功能,再实现继承 View
子类(比如 TextView
/ Button
等)实现功能,最后是如果实现自定义布局
视图基本方法
自定义视图主要分两个部分,绘图(Drawing
) 和 事件处理(event handling
)
Android
通过一套流程来绘制视图,View
类也提供了标准方法(standard methods
)
Note:以上为最常用的标准方法,实际开发过程中不需要重载全部方法,仅需根据自身需求选择合适的方法即可
绘图(drawing
)浅析
drawing
)浅析
针对绘图部分,基本的实现流程如下:
构造函数 -> 自定义属性 -> 尺寸测量 -> 绘图
其中还会遇到属性改变和尺寸改变的情况
事件处理(event handling
)浅析
event handling
)浅析
未完待续…
位置和大小
视图在屏幕上占据一块矩形(rectangle
)区域(对于那些屏幕上的圆型图案,只是将四周透明化了),为了描述视图在屏幕上的位置(position
)和大小(dimension
),系统提供了一些函数方便计算
Note:屏幕坐标系和数学坐标系不一样,它的 X
轴正方向为水平向左,Y
轴正方向为垂直向下。参考:安卓自定义View基础-坐标系
位置
定位一个视图在屏幕坐标系上的位置,最重要的是左侧坐标(left coordinate
,即 Y
轴坐标)和顶部坐标(top coordinate
,即 X
轴坐标),通过方法 getLeft()
和 getTop()
获得
/**
* Left position of this view relative to its parent.
*
* @return The left edge of this view, in pixels.
*/
@ViewDebug.CapturedViewProperty
public final int getLeft() {
return mLeft;
}
/**
* Top position of this view relative to its parent.
*
* @return The top of this view, in pixels.
*/
@ViewDebug.CapturedViewProperty
public final int getTop() {
return mTop;
}
Note:以上两个方法得到是视图相对于父视图的位置。视图层次结构是一个树状图,每一层视图得到自身大小后,再赋予下一层子视图位置
同样的,还可以得到视图右侧的坐标(getRight()
)和底部的坐标(getBottom()
),它们之间的关系如下:
getRight() = getLeft() + getWidth()
getBottom() = getTop() + getHeight()
方法 getWidth
和 getHeight
需要在绘制阶段调用;同理,getRight
和 getBottom
在绘制阶段调用
大小
视图提供了两组方法来获取宽和高:
getMeasuredWidth()
和getMeasuredHeight()
getWidth()
和getHeight()
第一组方法能得到 测量宽度(Measured Width
) 和 测量高度(Measured Height
),其值表示子视图想要在父视图中占据多大的尺寸(define how big a view wants to be within its parent
):
/**
* Like {@link #getMeasuredWidthAndState()}, but only returns the
* raw width component (that is the result is masked by
* {@link #MEASURED_SIZE_MASK}).
*
* @return The raw measured width of this view.
*/
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
/**
* Like {@link #getMeasuredHeightAndState()}, but only returns the
* raw width component (that is the result is masked by
* {@link #MEASURED_SIZE_MASK}).
*
* @return The raw measured height of this view.
*/
public final int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
}
第二组方法得到的就是实际的宽度(width
,也称为 drawing width
)和实际的高度(height
,也称为 drawing height
),其值表示的是视图在屏幕上的实际大小:
/**
* Return the width of the your view.
*
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
/**
* Return the height of your view.
*
* @return The height of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
return mBottom - mTop;
}
Note 1:二组方法得到的宽和高并不一定不一致
Note 2:位置和尺寸方法得到的值以 像素 为单位(The unit for location and dimensions is the pixel
)
Note 3:在 onMeasure
方法后调用测量宽度(measured width
)和测量高度(measured height
)
Note 4:在布局完成后,绘制过程中调用实际宽度和实际高度