为什么要自定义View?
系统配置的View满足不了我们的需求,我们需要针对业务制作一个自己的View。
自定义一般View步骤
在values目录下新建一个first.xml文件,编写内容如下:
1 2 3 4 5
| <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="first_test"> </declare-styleable> </resources>
|
这个是我们给自定义View设置的属性文件,但是我们现在只是把styleable命名为first_test,其他一个属性留到后面再说。
新建一个java文件继承于View
1 2 3 4 5 6 7 8
| public class FirstTest extends View { public FirstTest(Context context) { super(context); } public FirstTest(Context context, AttributeSet first) { super(context, first); } }
|
这里之重写了View的两个构造函数(View不止这两个构造函数,但是这两个是必写的,其他的后面会讲)
然后把我们的view加入我们的布局当中
1 2 3 4 5
| <com.example.a73233.test.FirstTest android:layout_width="match_parent" android:layout_height="200dp" android:background="#00BCD4" />
|
这里我们给他设置了三个系统属性,宽度和高度,以及背景颜色。
是的,这样自定义view的初步工作就完成了。但是显然这个View是空空的,啥也没有。。。好,我们回到Java文件给他搞点东西。
我们看到重写了View的一个构造函数,但显然是不够的。那么我们还要重写什么函数呢?如下:
onMeasure()
1 2 3 4 5 6 7 8 9 10
| public class FirstTest extends View { public FirstTest(Context context) { super(context); }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
|
onMeasure() 函数的功能负责测量我们View的宽高尺寸 这里出现了两个参数widthMeasureSpec 和heightMeasureSpec 。
这两个有点眼熟的其实就是我们从刚刚的布局中拿到的宽度和高度 。
以heightMeasureSpec为例,虽然它是一个参数,但是它其实携带了两个量。
其一为我们拿到的view的高度,其二为测量模式。
1 2
| int heightSize = MeasureSpec.getMode(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
这个测量模式有三种:
测量模式 |
二进制数值 |
描述 |
UNSPECIFIED |
00 |
默认值,父控件没有给子view任何限制,子View可以设置为任意大小。 |
EXACTLY |
01 |
表示父控件已经确切的指定了子View的大小。 |
AT_MOST |
10 |
表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小。 |
理论知识就是这些,因为我们要做的是一个正方形的view ,我们添加多一点代码来完成这个onMeasure() 函数。代码比较简单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| private int getSize(int defaultSize, int measureSpec) { int mySize = defaultSize;
int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec);
switch (mode) { case MeasureSpec.UNSPECIFIED: { mySize = defaultSize; break; } case MeasureSpec.AT_MOST: { mySize = size; break; } case MeasureSpec.EXACTLY: { mySize = size; break; } } return mySize; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightSize = getSize(200,heightMeasureSpec); int widthtSize = getSize(200,widthMeasureSpec);
if(heightSize > widthtSize){ heightSize = widthtSize; }else { widthtSize = heightSize; }
setMeasuredDimension(widthtSize,heightSize); }
|
好了到这里我们可以运行一下了。
ok,正方形的view。那么问题又来了,如果我们想定义的是一个圆形的View呢?这就涉及到下面重写的这个函数了。
onDraw()
1 2 3 4
| @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); }
|
接下来重写的这个onDraw()函数大有来头,显而易见,上面我们把view的宽高测出来了,这个函数就把我们想要的效果画出来。
我们直接上代码(看注释):
1 2 3 4 5 6 7 8 9 10 11
| @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas);
Paint paint = new Paint(); paint.setColor(Color.BLACK); paint.setStrokeWidth(10f); paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300,300,300, paint); }
|
在这里你可以发现明明onDraw函数画的是一个圆,怎么刚才的正方形还在?那是因为在onMeasure()函数我们确定了控件大小(占地面积),但是画圆的时候没用完,所以还能看见我们在布局文件设定的背景颜色,我们把背景色去掉就只剩下一个圆了。
自定义view就这样?
怎么可能?上面只是画一个圆来简单的介绍了自定义一个view的步骤。
- 还记得我们一开始新建了一个view的firs.xml的文件吧,我没还可以给这个view添加更多的属性。看这里
- 还有我们在重写view的onDraw()函数的时候,设置了画笔的属性,那么画笔还有那些属性呢?
- 除了圆,我们还可以画其他的图形,怎么画呢?看这里
博客参考各种网络资源,侵删,若有错误,恳请指正。