标签:
如何自定义控件主要分为以下几个步骤:
1、自定义属性的声明与获取
(1)分析需要的自定义属性
(2)在res/values/attrs.xml定义声明,如
<resources> <declare-styleable name="HookView"> <attr name="loadingText" format="string" /> <attr name="completeText" format="string" /> <attr name="textSize" format="dimension" /> </declare-styleable> </resources>
(3)在layout xml文件中进行使用
定义了名称然后3个属性,然后在自定义控件引用时候加上
xmlns:app="http://schemas.android.com/apk/res-auto"
(4)在view的构造方法中进行获取
在自定义控件中利用TypedArray获取所设置的属性
if (attrs != null) { //获取自定义属性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HookView); if (ta.hasValue(R.styleable.HookView_loadingText)) loadingText = ta.getString(R.styleable.HookView_loadingText); if (ta.hasValue(R.styleable.HookView_completeText)) completeText = ta.getString(R.styleable.HookView_completeText); textSize = ta.getDimension(R.styleable.HookView_textSize, 20); ta.recycle(); }
2、测量onMewsure(占多大空间)
通常View在屏幕上显示出来要先经过measure然后到layout. 在调用onMeasure(int widthSpec, int heightSpec)方法时,要涉及到MeasureSpec的使用
MeasureSpec有3种模式分别是【UNSPECIFIED】 、【EXACTLY】、【AT_MOST】
当我们设置width或height为fill_parent时,容器在布局时调用子 view的measure方法传入的模式是EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的。
而当设置为 wrap_content时,容器传进去的是AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸。当子view的大小设置为精确值时,容器传入的是EXACTLY, 而MeasureSpec的UNSPECIFIED模式通常是滑动控件中给子控件View的onMeasure方法默认行为是当模式为UNSPECIFIED时,设置尺寸为mMinWidth(通常为0)或者背景drawable的最小尺寸。
形象的说来,onMeasure方法在父元素正要放置该控件时调用.父控件调用它向子控件问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec.它们指明控件可获得的空间以及关于这个空间描述的元数据.比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里.
下面看一下经典的View中onMeasure实现
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredHeight = measureHeight(heightMeasureSpec); int measuredWidth = measureWidth(widthMeasureSpec); setMeasuredDimension(measuredHeight, measuredWidth); } private int measureHeight(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); // Default size if no limits are specified. int result = 500; if (specMode == MeasureSpec.AT_MOST){ // Calculate the ideal size of your // control within this maximum size. // If your control fills the available // space return the outer bound. result = specSize; } else if (specMode == MeasureSpec.EXACTLY){ // If your control can fit within these bounds return that value. result = specSize; } return result; } private int measureWidth(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); // Default size if no limits are specified. int result = 500; if (specMode == MeasureSpec.AT_MOST){ // Calculate the ideal size of your control // within this maximum size. // If your control fills the available space // return the outer bound. result = specSize; } else if (specMode == MeasureSpec.EXACTLY){ // If your control can fit within these bounds return that value. result = specSize; } return result; }
关于MeasureSpec类这里多说一点,
它代表一个32位的int值,高2位代表specMode(测量模式),低30位代表specsize(某种测量模式下的规格大小)
关于它的常用三个函数:
static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)
static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)
static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)
3、布局onLayout(放在什么位置)
4、绘制onDraw(绘制什么)
通常需要熟练掌握 canvas、Path的API,可能还有属性动画的交互
5、onTouchEvent
需要做用户交互,对Event事件的捕捉处理
6、onInterceptTouchEvent(viewgroup)
viewgroup中事件拦截,这里需要掌握事件分发机制
入门主要是这几块内容,每一块都需要深入理解,在看到一个自定义view源码时往往就是通过这几块往下分析
标签:
原文地址:http://blog.csdn.net/xsf50717/article/details/50491824