安卓自定义弧形刻度选择器

来源:互联网 发布:华硕mg248q设置软件 编辑:程序博客网 时间:2024/05/20 00:36

在android开发过程中实现通过自定义View实现的弧形刻度选择器,效果如下。

1db70005fc7182d81a02

演示效果

使用方法:

1,布局文件添加以下属性

app:arcLineColor="#ff0000" 弧线颜色

app:drawLineSpace="1" 刻度线间距

app:drawTextSpace="5" 刻度值间隔

app:everyScaleValue="1" 滑动像素与刻度值的比例

app:indicatorColor="#1cffaf" 中间指示器颜色

app:scaleLineColor="#0000ff" 刻度线颜色

app:scaleMin="200" 选择器最小值

app:scaleNum="30" 刻度数量

app:scaleSpace="1" 刻度间距

app:scaleTextColor="#0000ff" 刻度值颜色

app:scaleUnit="单位" 刻度单位

app:selectTextColor="#111111" 选中值颜色

2,setSelectScaleListener()监听刻度值变化



一,测量:

      首先在onMeasure方法中通过测量获取当前View的宽高,中心点,半径

mWidth = getMeasuredWidth();mHeight = getMeasuredHeight();mCenterX = mWidth / 2;mCenterY = 0;radius = Math.min(mWidth / 2 - padding, mHeight / 2 - padding);


二,画弧形:

绘制弧形可以通过canvas.drawPath或通过canvas.drawArc()方法绘制弧形,本文中选择通过canvas.drawPath方法绘制,下文会讲解为什么使用drawPath方法绘制弧形

int top = -radius;int bottom = radius;int left = mCenterX - radius;int right = mCenterX + radius;mArcPath.reset();mArcPath.addArc(new RectF(left, top, right, bottom), 0, 180);canvas.drawPath(mArcPath, mArcPaint);


三,绘制弧形刻度线以及刻度:

怎么样绘制刻度线呢??通过观察发现刻度线是垂直于弧形切线的,所以绘制刻度线我们首先要知道这些刻度点当前的切线。PathMeasure.getPosTan()可以获取到Path上某个点的xy坐标与tan值,PathMeasure.getLength可以获取Path的长度,getLength与getPosTan方法配合使用获取Path线上点的pos与tan值,获取到坐标值与tan值之后我们就可以绘制刻度线了。PathMeasure办法只能作用在Path上,这也就解释了为什么选择drawPath的方法绘制弧形。

PathMeasure mPathMeasure = new PathMeasure();mPathMeasure.setPath(mArcPath, false);float[] pos = new float[2];float[] tan = new float[2];for (int i = 1; i <= mScaleNumber; i++) {  //mScaleNumber为刻度点的数量,for循环遍历刻度点float percentage = i / (float) mScaleNumber;//计算当前刻度所占总刻度的百分比,mPathMeasure.getPosTan(mPathMeasure.getLength() * percentage, pos, tan);//通过上一步计算出来的百分比可以计算出当前刻度在Path上的位置
    double atan2 = Math.atan2(tan[1], tan[0]);
    double angle = calcArcAngle(atan2) + 90;//刻度线垂直切线,所以旋转90°int scale = Math.round(currentAngle + mScaleMin + i * mScaleSpace);    if (scale >= mScaleMin && scale % mDrawLineSpace == 0) {float startX = pos[0];        float startY = pos[1];        float endX = 0;        float endY = pos[1];        if (scale % mDrawTextSpace == 0) {endX = pos[0] + 80;mScaleLinePaint.setStrokeWidth(15);mScaleLinePaint.setColor(mScaleLineColor);            if (currentAngle >= (-(mScaleNumber / 2) * mScaleSpace)) {canvas.save();canvas.rotate((float) (angle + 90), pos[0], pos[1]);String mScaleText = scale + mScaleUnit;                float scaleTextLength = mScaleTextPaint.measureText(mScaleText, 0, mScaleText.length());canvas.drawText(mScaleText, pos[0] - scaleTextLength / 2, pos[1] - 130, mScaleTextPaint);canvas.restore();}} else if (scale % mDrawLineSpace == 0) {mScaleLinePaint.setColor(mScaleTextColor);mScaleLinePaint.setStrokeWidth(10);endX = pos[0] + 50;}canvas.save();canvas.rotate((float) angle, pos[0], pos[1]);canvas.drawLine(startX, startY, endX, endY, mScaleLinePaint);canvas.restore();}}


四,绘制当前选中的刻度值

String selectedScaleText = selectedScale + mScaleUnit;float selectedScaleTextLength = mSelectedTextPaint.measureText(selectedScaleText, 0, selectedScaleText.length());canvas.drawText(selectedScaleText, mCenterX - selectedScaleTextLength / 2, mCenterY + 100, mSelectedTextPaint);


五,绘制中心指示器:

中心指示器位于弧形中心,所以首先获取要弧形中心点坐标,tan值。弧形中心点也就是弧形长度的一半也就是mPathMeasure.getLength() * (0.5f)。通过坐标以及tan值使用drawLine方法绘制直线drawPath方法绘制剪头

PathMeasure mPathMeasure = new PathMeasure();mPathMeasure.setPath(mArcPath, false);float[] tan = new float[2];float[] pos = new float[2];mPathMeasure.getPosTan(mPathMeasure.getLength() * (0.5f), pos, tan);canvas.save();double angle = calcArcAngle(Math.atan2(tan[1], tan[0])) + 90;canvas.rotate((float) angle, pos[0], pos[1]);//画直线canvas.drawLine(pos[0], pos[1], pos[0] + 80, pos[1], mIndicatorPaint);Path linePath = new Path();//画箭头linePath.moveTo(pos[0] + 80, pos[1] - 20);linePath.lineTo(pos[0] + 80 + 20, pos[1]);linePath.lineTo(pos[0] + 80, pos[1] + 20);canvas.drawPath(linePath, mIndicatorPaint);canvas.restore();


六,为了美观画弧形两侧的遮罩层

弧形的两侧有一个有白色到透明的线性渐变,LinearGradient类可以实现画笔的线性渐变。

//画左侧遮罩LinearGradient mLeftLinearGradient = new LinearGradient(0, 0, mCenterX - 100, 150, Color.WHITE, Color.TRANSPARENT, Shader.TileMode.CLAMP);mMaskPaint.setShader(mLeftLinearGradient);canvas.drawPath(mArcPath, mMaskPaint);//画右侧遮罩LinearGradient rightLinearGradient = new LinearGradient(mWidth, 0, mCenterX + 100, 150, Color.WHITE, Color.TRANSPARENT, Shader.TileMode.CLAMP);mMaskPaint.setShader(rightLinearGradient);canvas.drawPath(mArcPath, mMaskPaint);canvas.drawPath(mArcPath, mArcPaint);


七,计算滑动旋转角度:

当触摸屏幕的时候会有横向的坐标x与纵向的坐标值y,x与y与中点会产生一个三角形,通过三角函数定理以及当前坐标我们可以获取到sin值,通过反三角函数Math.asin方法通过sin值我们可以计算出滑动角度。滑动过程中会角度会不断变化,角度变化的差值就决定了选择器的增加或减少。

触摸屏幕对角度的处理

switch (event.getAction()) {case MotionEvent.ACTION_DOWN:float mDownX = event.getX();        float mDownY = event.getY();initAngle = computeAngle(mDownX, mDownY);        if (mDownX > mCenterX) {initAngle = 180 - initAngle;}break;    case MotionEvent.ACTION_MOVE:float mTouchX = event.getX();        float mTouchY = event.getY();        if (isTouch(mTouchX, mTouchY)) {double moveAngle = computeAngle(mTouchX, mTouchY);            if (mTouchX > mCenterX) {moveAngle = 180 - moveAngle;}double tempAngle = moveAngle - initAngle;            long addValue = Math.round(tempAngle * mEvenyScaleValue);currentAngle += addValue;            if (currentAngle >= -(mScaleNumber / 2) * mScaleSpace) {invalidate();} else {currentAngle = currentAngle - addValue;}initAngle = moveAngle;            return true;}break;}


计算角度

private double computeAngle(float touchX, float touchY) {double atan2 = Math.atan2(touchY, touchX);    double taperedEdge = Math.sqrt(Math.pow(touchX - mCenterX, 2) + Math.pow(touchY - mCenterY, 2));//计算斜边double sin = (touchY - mCenterY) / taperedEdge;    double asin = Math.asin(sin);    double calcArcAngle = calcArcAngle(asin);    return calcArcAngle;}


本文讲解了如何实现一个弧形刻度选择器,由于本人能力有限,必然存在很多不足,忘大家见解

​大家可以到https://github.com/yhongm/ArcScaleView下载完整的代码,在代码中有详细的注释。欢迎大家fork,star

3 0