View的基本知识梳理:
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
方法是测量我们自身大小,当我们测量出自身的大小时,可以通过setMeasuredDimension(width,height);来将我们测量出来的值设置给我们自身,如果我们没有调用setMeasuredDimension(width,height)这个方法的话,将会报错。
onDraw(Canvas canvas)
这个方法是我们view的绘制方法,基本我们view的展示以及其他效果都是在这个方法中绘制,因为每次绘制都会比较消耗内存,所以我们尽量绘制少点东西。
onLayout(boolean changed, int left, int top, int right, int bottom)
这个方法是摆放控件的方法,在view里面我们用的比较少,主要是在viewGroup在使用。
其实自定义view并不困难,主要是数学知识和API知识即可,因为有一些方便的API已经帮我们定义了一些好用的工具或者方法。
因为公司最近需要一个圆形view控件,然后发现其他都不适应,所以只好自己重新写一个。
分析:当我们拿到UI给的UI图的时候,先不急着做,先分析分析要这么做,这样可以让我们事半功倍,在这里我主要用到的方法是遮挡法,因为底部条是渐变,给了图片的缘故,所以不能自己绘制底部条,要使用UI给的图片,所以我就考虑在底部条的位置上绘制一条遮挡条来遮挡我们的底部,分析结束,我们就可以开始做了。
PorterDuff.Mode.DST_IN 这种模式可以在我们背景图和源图相交的地方绘制,本来一开始是打算用这种模式来绘制我们的view的,然后发现这种只能在cancvs上绘制一次,不能恢复路径,所以就放弃这种方法了
首先我们获取我们的背景图和源图
bitmapSrc = BitmapFactory.decodeResource(getResources(), R.drawable.home_round_colorful);
bitmapBg = BitmapFactory.decodeResource(getResources(), R.drawable.home_round_white).copy(Bitmap.Config.ARGB_8888, true);
在onDraw方法在我们绘制出所需的图片
canvas.drawBitmap(bitmapCircle,0,0,null);
canvas.drawBitmap(bitmapBg,0,0,null);
然后我们将要绘制圆弧的矩形给确定下来:
/**
* 用于定义的圆弧的形状和大小的界限
* @return
*/
private RectF getCirclePointRectF(){
RectF oval = new RectF(bitmapSrc.getWidth() /2- (int)bitmapSrc.getWidth() / 2.85f ,bitmapSrc.getWidth() /2-(int)bitmapSrc.getWidth() / 2.85f - 3,bitmapSrc.getWidth() /2+(int)bitmapSrc.getWidth() / 2.85f,bitmapSrc.getHeight()/2+(int)bitmapSrc.getWidth() / 2.85f-5);
return oval;
}
这矩形将确定我们绘制圆弧的路径,至于圆心位置,我们自己可以定到,
到这里我们确定了整个圆的背景和绘制的路径看。接下来就是将我们手指在屏幕上的触碰转化为角度就可以了。
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
if (isCanTouch && (event.getAction() == MotionEvent.ACTION_MOVE || isTouch(x, y))) {
// 通过当前触摸点搞到cos角度值
float cos =computeCos(x, y);
// 通过反三角函数获得角度值
if (x < getWidth() / 2) { // 滑动超过180度
mCurAngle = Math.PI * RADIAN + Math.acos(cos) * RADIAN;
} else { // 没有超过180度
mCurAngle = Math.PI * RADIAN - Math.acos(cos) * RADIAN;
}
mCurProcess = getSelectedValue();
refershWheelCurPosition(cos);
if (mChangListener != null) {
mChangListener.onChanged(this, mCurProcess);
}
invalidate();
return true;
} else {
return super.onTouchEvent(event);
}
}
我们通过触摸的x,y值通过computeCos()函数来转为cos值,这里将运用一点三角函数和圆的知识。
/**
* 拿到倾斜的cos值
*/
private float computeCos(float x, float y) {
float width = x - getWidth() / 2;
float height = y - getHeight() / 2;
float slope = (float) Math.sqrt(width * width + height * height);
return height / slope;
}
这里我们得到的角度是在圆弧边上形成的角度,通过反三角函数我们可以得到我们所需的角度了,至于圆标的绘制,我们则通过得到的角度与半径就可以计算出圆标的坐标数直接绘制即可。
private void refershWheelCurPosition(double cos) {
mWheelCurX = calcXLocationInWheel(360f - mCurAngle,cos);
mWheelCurY = calcYLocationInWheel(cos);
}
private float calcXLocationInWheel(double angle, double cos) {
if (angle < 180) {
return (float) (getWidth() / 2 + (-Math.sqrt(1 - cos * cos)) * mUnreachedRadius);
} else {
return (float) (getWidth() / 2 - (-Math.sqrt(1 - cos * cos)) * mUnreachedRadius);
}
}
在这里,我们绘制将是瞬时间绘制,这明显和我们的不符,我们想要逆时间绘制,在这里其实只要把角度取反,我们的绘制将会变成逆时间绘制,可是会有个问题就是,手势与圆的绘制方向将会不一致,这给别人一种很别扭的感觉,所以我们要让手势与圆的绘制方向一致,在这里我们只需:
mPath.addArc(getCirclePointRectF(),270, -(360f -(float) mCurAngle) );//自己画图很容易就看出来了
自己画图很容易就能看出为什么了,到这里,自定义圆形控件算了完成了。