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

恢弘志士之气,不宜妄自菲薄。这篇文章主要讲述安卓自定义弧形刻度选择器相关的知识,希望能为你提供帮助。
在android开发过程中实现通过自定义View实现的弧形刻度选择器,效果如下。

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

文章图片

演示效果

一,测量:
【安卓自定义弧形刻度选择器】      首先在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 = https://www.songbingjia.com/android/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




















    推荐阅读