自定义View
自定义View
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()
View 类默认的 onMeasure() 方法只支持 EXCATLY 模式。
如果要让自定义的 View 支持 wrap_content 属性,就必须重写 onMeasure() 方法来指定控件 wrap_content 时的大小
在方法最后,要把测量后的宽高值作为参数设置给 setMeasureDimension() 方法
测量控件的宽高
从宽、高的 MeasureSpec 对象中提取宽、高具体的测量模式和测量大小
如果是 EXACTLY 模式,直接使用测量的大小,即第 1 步获取的测量大小
如果是 AT_MOST 模式,先指定一最小值,然后从取测量大小和最小值中间较小的数
如果是 UNSPECIFIED 模式,直接返回指定的最小值
自定义View
重写 onDraw() 方法
View 类重点方法介绍
分类 | 方法 | 说明 |
---|---|---|
构造 | Constructor | 有一种形式的构造方法是使用代码创建的时候调用的,另一种形式是View被布局文件填充时被调用的。第二种形式应该解析和使用一些属性定义在布局文件中 |
onFinishInflate() | 当View和他的所有子控件被XML布局文件填充完成时被调用。(这个方法里面可以完成一些初始化,比如初始化子控件) | |
布局 | onMeasure(int, int) | 当决定view和他的孩子的尺寸需求时被调用(也就是测量控件大小时调用) |
onLayout(boolean, int, int, int, int) | 当View给他的孩子分配大小和位置的时候调用(摆放子控件) | |
onSizeChanged(int, int, int, int) | 当view大小发生变化时调用 | |
绘制 | onDraw(android.graphics.Canvas) | 当视图应该呈现其内容时调用(绘制) |
事件处理 | onKeyDown(int, KeyEvent) | 按键时被调用 |
onKeyUp(int, KeyEvent) | 按键被抬起时调用 | |
onTrackballEvent(MotionEvent) | Called when a trackball motion event occurs | |
onTouchEvent(MotionEvent) | 触摸屏幕时调用 | |
焦点 | onFocusChanged(boolean, int, android.graphics.Rect) | 获取到或者失去焦点是调用 |
onWindowFocusChanged(boolean) | 窗口获取或者失去焦点是调用 | |
Attaching | onAttachedToWindow() | 当视图被连接到一个窗口时调用 |
onDetachedFromWindow() | 当视图从窗口分离时调用 | |
onWindowVisibilityChanged(int) | 当View的窗口的可见性发生改变时调用 |
实现自定义 View 的三种方法
对现有控件进行扩展
通过组合来实现新的控件
重写 View 实现全新的控件
View 的三个构造方法
public View (Context context) :从代码中创建 view 时调用。当不需要使用xml声明或者不需要使用inflate动态加载时候,实现此构造函数即可 。 如果是从xml填充的视图,就不会调用这个
public View (Context context, AttributeSet attrs) :当需要在 xml 中声明此控件,则需要实现此构造函数。并且在构造函数中把自定义的属性与控件的数据成员连接起来,这个是在 xml 创建但是没有指定 style 的时候被调用, 即写在 xml 里的 调用 2 个参数的 attr 里边传过来的是 xml 里边对应的 height width 等参数,包括自己定义的参数
public View (Context context, AttributeSet attrs, int defStyle) :接受一个style资源
自定义属性
xml 中定义
在 res / values / 目录下创建 attrs.xml 的属性定义文件
在 attrs.xml 文件中,使用 <declare-styleable> 标签来声明使用自定义属性,attr 标签来声明具体的自定义属性。attr 标签中,name 属性用于指定自定义属性的名称,format 属性用来指定属性的类型
示例
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="TopBar"> <attr name="title" format="string" /> <attr name="titleTextSize" format="dimension" /> <attr name="titleTextColor" format="color" /> <attr name="leftTextColor" format="color" /> <attr name="leftBackground" format="reference|color" /> <attr name="rightTextColor" format="color" /> <attr name="rightBackground" format="reference|color" /> <attr name="rightText" format="string" /> </declare-styleable> </resources>
layout 文件中使用
为了避免重复使用长的 URI,使用 xmlns 指令,为命名空间指定一个别名
使用:别名:自定义属性名=“属性值” 的方式,在布局文件中使用自定义属性
示例:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:topBar="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.components.TopBar
android:layout_width="match_parent"
android:layout_height="50dp"
topBar:leftText="返回"
topBar:hasRightImage="true"
topBar:hasRightText="true"
topBar:midText="辛辛苦苦做程序"
topBar:rightText="管理"
/>
</LinearLayout>
Java 文件中获取
-
通过两个、三个或四个参数的构造函数获取
-
TypedArray ta = context.[getTheme().]obtainStyleAttributes(attrs, R.styleable.自定义样式名称); 这句的意思是,将在 attrs.xml 中定义的 declare-styleable 中的所有属性的值存储到 TypedArray 中
-
title = ta.getString(R.styleable.自定义样式名称_自定义属性名称); 这句的意思是,从 TypedArray 中取出对应的自定义的值
-
ta.recycle(); 这句的意思是,获取完 TypedArray 的值后,对资源进行回收,避免重新创建时的错误
-
示例:
public TopBar(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TopBar); mLeftStr = typedArray.getString(R.styleable.TopBar_leftText); mMidStr = typedArray.getString(R.styleable.TopBar_midText); hasRightIv = typedArray.getBoolean(R.styleable.TopBar_hasRightImage, false); hasRightTv = typedArray.getBoolean(R.styleable.TopBar_hasRightText, false); mRightStr = typedArray.getString(R.styleable.TopBar_rightText); typedArray.recycle(); }
自定义绘制
View 第一次被指定尺寸,或 View 尺寸发生变化,进入 onSizeChanged() 方法
注意:在计算 View 尺寸时,必须自己处理 padding
如果是通过组合实现新的控件,在构造函数中完成处理即可
如果是自定义新的控件,在 onDraw() 方法中完成绘制