一、自定义控件基础
1.坐标系
image.pngimage.png
public class CustomView1 extends View {
public CustomView1(Context context) {
super(context);
}
public CustomView1(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
int top = getTop(); //获取子View左上角距父View顶部的距离
int left = getLeft(); //获取子View左上角距父View左侧的距离
int bottom = getBottom(); //获取子View右下角距父View顶部的距离
int right = getRight(); //获取子View右下角距父View左侧的距离
}
@Override
public boolean onTouchEvent(MotionEvent event) {
event.getX();//触摸点相对于其所在组件坐标系的坐标
event.getY();
event.getRawX(); //触摸点相对于屏幕默认坐标系的坐标
event.getRawY();
return super.onTouchEvent(event);
}
}
2.角度弧度
image.png image.png角度和弧度的换算关系:
圆一周对应的角度为360度(角度),对应的弧度为2π弧度。
故得等价关系:360(角度) = 2π(弧度) ==> 180(角度) = π(弧度)
由等价关系可得如下换算公式:
rad 是弧度, deg 是角度,rad = deg x π / 180.
二、自定义控件流程
image.png1.构造函数
构造函数式View的入口,可以用来初始化一些内容和获取自定义属性。
View的构造函数有四种重载,分别是一到四个参数。其中有三个参数和有四个参数的构造函数暂不考虑,有三个参数的构造函数即使你使用了Style这个属性,最后所调用的依然是两个参数的构造函数,而有四个参数的构造函数在API21的时候才添加上。
public class CustomView1 extends View {
public CustomView1(Context context) {
super(context);
}
public CustomView1(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
2.测量View大小(onMeasure)
View的大小不仅由自身决定,同事也会受到父控件的影响,为了我们的控件能更好的适应各种情况,一般会自己进行测量。我们可以从onMeasure的两个参数中取出宽高的相关数据。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthsize = MeasureSpec.getSize(widthMeasureSpec); //取出宽度的确切数值
int widthmode = MeasureSpec.getMode(widthMeasureSpec);//取出宽度的测量模式
int heightsize = MeasureSpec.getSize(heightMeasureSpec);//取出高度的确切数值
int heightmode = MeasureSpec.getMode(heightMeasureSpec);//取出高度的测量模式
}
onMeasure的两个int参数其实并不是宽和高,而是由宽、高和各自方向上对应的测量模式来合成的一个值。测量模式有三种,它被定义在View.MeasureSpec类中:
image.png
3.确定View的大小(onSizeChanged)
这个函数在试图大小发生改变的时候调用。在测量完View并使用setMeasuredDimension函数之后View的大小基本上已经确定了,那为什么还要再次确定View的大小呢?这是因为View的大小不进又View本身控制,而且受父控件的影响(比如模式设置为AT_MOST,那么可能我们虽然设置了宽高但是当他大于父控件宽高的时候就出现了上限了),所以我们在确定View大小的时候最好使用系统提供的onSizeChanged回调函数。其中第一和第二个参数就是View最终的宽高了。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
4.确定子View布局位置(onLayout)
确定布局的函数式onLayout,它用于确定子View的位置,在自定义ViewGroup中会用到,他调用的是子View的Layout函数。在自定义ViewGroup中,onLayout一般是循环取出子View,然后经过计算得出各个子View位置的坐标值,然后用child.layout(l,t,r,b);设置子View的位置。
image.png
5.绘制内容(onDraw)
onDraw是实际绘制的部分,也就是我们真正关心的部分,使用的是Canvas绘图。Canvas我们可以称之为画布,能够在上面绘制各种东西。
image.png@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//1.绘制点
mPaint.setColor(Color.RED);
canvas.drawPoint(50, 50, mPaint);
canvas.drawPoints(new float[]{
60, 60,
70, 70,
80, 80
}, mPaint);
//2.在坐标(300,300)(500,600)之间绘制一条直线
canvas.drawLine(50, 50, 500, 60, mPaint);
//3.绘制矩形
/**
* Canvas提供了三种重载方法,第一种就是提供四个数值(矩形左上角和右下角两个点的坐标)来确定一个矩形进行绘制。
* 其余两种是先将矩形封装为Rect或RectF(实际上仍然是用两个坐标点来确定的矩形),然后传递给Canvas绘制
* Rect和RectF两者最大的区别就是精度不同,Rect是int(整形)的,而RectF是float(单精度浮点型)的
*/
mPaint.setColor(Color.GRAY);
canvas.drawRect(100, 100, 800, 400, mPaint);
Rect rect = new Rect(100, 100, 800, 400);
canvas.drawRect(rect, mPaint);
RectF rectF = new RectF(100, 100, 800, 400);
canvas.drawRect(rectF, mPaint);
//4.绘制圆 (绘制圆形有四个参数,前两个是圆心坐标,第三个是半径,最后一个是画笔。)
mPaint.setColor(Color.GREEN);
canvas.drawCircle(500,500,400,mPaint); // 绘制一个圆心坐标在(500,500),半径为400 的圆。
//5.绘制圆弧
/**
* 相比于绘制椭圆,绘制圆弧还多了三个参数:
* startAngle // 开始角度
sweepAngle // 扫过角度
useCenter // 是否使用中心
使用了中心点之后绘制出来类似于一个扇形,而不使用中心点则是圆弧起始点和结束点之间的连线加上圆弧围成的图形。
*/
RectF rectF2 = new RectF(100,700,600,1200);
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF2,mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF2,0,90,true,mPaint);
//6.画笔的三种模式
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(40); //为了实验效果明显,特地设置描边宽度非常大
// 描边
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(200,200,100,paint);
// 填充
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(200,500,100,paint);
// 描边加填充
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(200, 800, 100, paint);
}
image.png
网友评论