项目中用到时间选择器,一个类似iOS里面健康睡眠设置时间的样式。样式如下:

相比于常规的自定义View,该时间控件主要是结合了触摸滑动的事件,还有比较重要的是对弧度的绘制,需要特别注意的是弧度和角度相关的转换。且需要将手指触摸绘制的轨迹在我们预想的位置绘制出完整的圆环。转换公式涉及到一些三角函数的使用。这里需要特别注意。
主要是通过自定义参数,在xml中设置一些常用的颜色,尺寸相关参数
背景部分绘制,包括整体的表盘绘制,刻度绘制,整点的文字绘制
//画外环
canvas.drawCircle(mCx, mCy, mRadius - mGapBetweenCircleAndLine * 2, mCirclePaint!!)
canvas.save()
//绘制刻度和文字
drawLineText(canvas)
绘制文字和刻度的时候需要通过旋转画布来保证绘制的文字方向正确。
旋转画布,调整弧度开始的位置,然后在表盘边缘绘制圆环。
drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)
进行弧度的绘制通过计算开始的弧度和扫过的弧度绘制。
保存画布后,绘制拖拽的滑块。
//通过不同条件绘制弧度
drawArcTrack(canvas, centerY)
如上绘制部分基本完成。后面主要逻辑都是在处理手势的部分。
首先在每个事件中都需要区分滑块1和滑块2,对每个滑块的事件区分处理。
核心方法将坐标点转换为弧度
// Use tri to cal radian
private fun getRadian(x: Float, y: Float): Float {
var alpha = Math.atan(((x - mCx) / (mCy - y)).toDouble()).toFloat()
// Quadrant
if (x > mCx && y > mCy) {
// 2
alpha += Math.PI.toFloat()
} else if (x < mCx && y > mCy) {
// 3
alpha += Math.PI.toFloat()
} else if (x < mCx && y < mCy) {
// 4
alpha = (2 * Math.PI + alpha).toFloat()
}
return alpha
}
该方法为非常核心的转换方法,通过计算点落在不同象限,获取到具体的弧度数值。
move事件中则是对绘制弧度的具体限制了。具体代码比较繁琐,可以根据自己的业务需求进行定制。
Log.d(TAG, "瞬间按下点的弧度$mPreRadian")
val temp = getRadian(event.x, event.y)
Log.d(TAG, "移动后的点的弧度" + temp + "关点弧度" + closeTime)
if (mPreRadian > Math.toRadians(270.0) && temp < Math.toRadians(90.0)) {
mPreRadian -= (2 * Math.PI).toFloat()
} else if (mPreRadian < Math.toRadians(90.0) && temp > Math.toRadians(270.0)) {
mPreRadian = (temp + (temp - 2 * Math.PI) - mPreRadian).toFloat()
}
mEndCurrentRadian += temp - mPreRadian
Log.d(TAG, "变更后的当前结束弧度:$mEndCurrentRadian")
mPreRadian = temp
后续就是我们添加的限制,比如我的限制为:不可以超过设置的起始时间,临近边界则不可滑动。
这里对两个滑块都要进行不同的判断,且需要对两个滑块增加限制,根据需求是否可以进行交叉等。
up事件中则是最终的结果,这里我们通过最后的弧度数值,进行换算来获取最后的时间。
比如,我的规则是每刻度是半个消息,这里就需要对滑动后的滑块进行矫正,让滑块落在刻度中心。将最后的数据结果通过实现注册的监听事件传递出去。
自定义view能更好的帮助我们熟悉view的绘制流程,对我们编码有很好的帮助。
项目地址,如果有需求可以参考实现:
GitHub - yihu5566/CircleAlarmTimer: 圆形的时间选择控件
基础来源