android 自定义控件

自定义控件的实现方式:

1. 组合各种控件,组合各种控件。

2. 修改已有控件,达到需求。

3. 继承View,实现需要的方法,完成自定义控件的设计。


自定义控件涉及到的几个关键方法,首先是自定义控件的三大方法:

1.onDraw()

onDraw是View进行显示界面的方法,我们常常在这个方法内部进行绘制控件的外观。涉及到几个比较重要的类:
1. Paint 画笔类,可设置颜色线条宽度等.

1.1 Paint
Paint 类是一个辅助类,我们常常使用来设置画布的显示效果,如颜色、遮照,图形的线的宽度等等。我们常用的api有:
setFlags(int flags)设置画笔的一些属性,如抗锯齿
setColor(int color)设置画笔的颜色
setStrokeWidth(float width)设置画笔的线宽
setStyle(Paint.Style style)设置画笔的样式,比如说是否填充图形
setTextSize(float textSize)设置画文字的大小
setXfermode(Xfermode xfermode)设置叠加模式

2. Canvas 画布类,提供了画各种图形的api

1.2 Canvas
CanVas画布类,提供画出2D图形以及相关操作的api,我们常使用的三类api和一些辅助类:

第一类,2d图形绘制:

canvas.drawRect 画正方形

canvas.drawBitmap画图片

canvas.drawLine 画线

canvas.drawText 画文字

canvas.drawOval 画椭圆

canvas.draArc 画圆弧

canvas.drawCircle 画圆

canvas.drawPath 画自定义路径

第二类,画布编辑: - canvas. translate 平移
canvas.scale 缩放
canvas.rotate 旋转
第三类,画布状态:
canvas. Save 保持画布状态
canvas.restore 恢复画布状态
辅助类:屏幕的一个区域
Rect(int left, int top, int right, int bottom)
RectF(float left, float top, float right, float bottom)


3. Bitmap 位图类,用它来获取图片信息
4. matrix 矩阵类,用它来控制图片的显示


2 onMeasure
在自定义控件的时候,我们常常需要确定一个控件的显示边界,onMeasure是android提供给我们的一个计算控件大小的方法,父控件会通过onMeasure
将他希望的布局参数传递给当前的View。
2.1 onMeasure的流程
首先根节点的measure方法会被调用,由于measure是view的final方法,子类是无法重写的,measure的方法体内会回调onMeasure,所以实际上测量的工
作是在onMeasure内完成的,而且由此我们可以知道measure是提供给外部调用的一个测量自己的方法。由于根节点大部分是ViewGroup,如LinerLayout
等,所以ViewGroup会在onMeasure方法内部逐一的调用子控件的measure方法,一直到最后的一个子控件。实际上是一个自顶向下的过程。

2.2 onMeasure(int widthMeasureSpec, int heightMeasureSpec)
根据查看源码发现,控件的大小是需要在onMeasure方法内部完成测量,那widthMeasureSpec与heightMeasureSpec这两个形参是什么
呢?我们在layout的布局文件内部的定义的layout_width与layout_height,与这两个参数有什么关联呢? 在进行测量之前我们要弄清楚几个问题:
1. 子控件显示在父控件的内部,大部分子控件的宽高被父控件的宽高限制。
2. 如ScrollView等控件,由于能够滑动,就不限制子控件的高度。
实际上layout_width,layout_height是控件向父控件申请的空间。我们常常有以下几种值可以选择:
match_parent
wrap_content
xxxdpi
这三种模式,我们可以分成两种模式,第一种是固定大小,match_parent,xxxdpi,match_parent是指与父控件一样大。另外一种是不固定大小的,
wrap_content,根据内容的大小进行显示。
我们设置在xml上面时,控件就能够根据这个设置进行更改大小,控件到底是如何获取到这个值得呢?
实际上控件是通过widthMeasureSpec和heightMeasureSpec获取到layout_width,layout_height,widthMeasureSpec和heightMeasureSpec是对
layout_width进行了一次包装,这个int值里面包含了两个值,一个是mode(模式),一个是size(大小)。
我们可以使用以下方法获取到widthMeasureSpec和heightMeasureSpec的值
MeasureSpec.getMode(measureSpec);//得到模式
MeasureSpec.getSize(measureSpec);//得到大小
MeasureSpec提供了三种类型:

1. MeasureSpec.AT_MOST
2. MeasureSpec.EXACTLY
3. MeasureSpec.UNSPECIFIED
这三种模式分别对应: MeasureSpec.AT_MOST--wrap_content,由控件内容来决定大小,但是最大不能超过父控件的大小。
MeasureSpec.EXACTLY--match_parent,xxxdpi,已经指定了特定的大小。match_parent 与父控件一样大,所以也是已经知道指定的大小
MeasureSpec.UNSPECIFIED,对控件的大小不做限制,控件可以要多大都行,比如说ScrollView的子控件。
最后根据widthMeasureSpec 和 heightMeasureSpec 计算出需要的大小后,需调用setMeasuredDimension才能起效。

2.3 View 常见的 onMeasuere 写法

private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) {
int result = desiredSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = Math.min(desiredSize, maxSize);
break;
case MeasureSpec.AT_MOST:
result = Math.min(Math.min(desiredSize, specSize), maxSize);
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = Math.min(desiredSize, maxSize);
break;
case MeasureSpec.AT_MOST:
result = Math.min(Math.min(desiredSize, specSize), maxSize);
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
desiredSize 是需要使用的尺寸,maxSize 是最大使用的尺寸,measureSpec 是onMeasure传进来的参数。
UNSPECIFIED,无限制尺寸,只要能满足控件的需求即可,所以比较 desiredSize 和 maxSize 的大小,去最小的即可
AT_MOST,wrapContent,也就是说这个控件最大不能超过父控件的大小,所以先比较desiredSize和specSize的大小,去最小的,再和maxSize比
较。
EXACTLY,指定尺寸的,直接返回specSize即可。

2.4 ViewGroup 常见的 onMeasuere 写法
viewGroup有义务对子控件的大小进行测量,ViewGroup提供了一下几个方法来调用子控件的自我测量
1. measureChildren(int widthMeasureSpec, int heightMeasureSpec);
2. measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec);
3. measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)
ViewGroup 的 onMeasuere 常常需要和onLayout一起使用,原因是在放置子控件的时候,我们常常是需要知道子控件的大小,才能够放到合适的位置
上。

3 onLayout
该方法是提供给ViewGroup来放置每个子控件的。通常我们需要配onMeasure来一起使用。
这个方法是每次布局大小变化,或者位置改变的时候都会本调用。
onLayout(boolean changed, int l, int t, int r, int b) changed 这个控件是否更改位置或者尺寸了
l 相对于父控件的左边的位置
t 相对于父控件的顶部的位置
r 相对于父控件的右边的位置
b 相对于父控件的下部的位置
我们必须在这个方法内部将子控件有序的放置在合适的位置。View 提供了llayout(int l, int t, int r, int b)方法给我们。


自定义控件的属性
我们在使用系统控件的时候,常常能够在xml上面进行配置,提供了这类属性的控件能够更加便于开发者的使用和维护,所以我们学习如何定义控件的属
性非常有必要。 下面就是自定义控件属性的步骤:
1. 在资源文件夹下新建文件attrs.xml
2. 在attrs.xml里面新建需要定义的属性
3. 新建一个declare-styleable 为这个属性增加一个name属性如 declare-styleable name="---"
4. 在declare-styleable的内部定义需要的属性,如 ,name是属性的名称,format是属性的类型
5. 在自定义控件接收自定义属性,在构造函数中使用Context获取TypedArray,obtainStyledAttributes(attrs, R.styleable.定义的属性)
6. 使用TypedArray.getxxx方法来获取相应的值,这个xxx就是定义在attr里面的类型,R.styleable.(declare-styleable name)_(attr name)
7. 在Xml使用
8. 定义自定义的命名空间,不同的IDE有不一样的处理方法,Eclipse xmlns:自定义名称 ="http://schemas.android.com/apk/res/包名" android
studio: xmlns:自定义名称 ="http://schemas.android.com/apk/res-auto"
需要注意的是:
format 的类型说明见下表
在自定义控件中获取属性,需使用类似格式R.styleable.(declare-styleable name)_(attr name)
format的类型
1. reference:参考某一资源ID。
2. color:颜色值
3. boolean:布尔值
4. dimension:尺寸值
5. float:浮点值。
6. integer:整型值。
7. string:字符串
8. fraction:百分数。
9. enum:枚举值
10. flag:位或运算



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值