android自定义View-HopPicker腕表

前一阵子看到了一块概念腕表,感觉非常有意思,就想能不能写一个在android手机上运行一下。本文地址:http://blog.csdn.net/qq_24505485/article/details/52717381

Hop Picker 的创意腕表

先上效果图:
android自定义View-HopPicker腕表_第1张图片
原理分析:和原作者实现的不一样,作者给的原理图是将刻度围绕黄色线条圈起的地方分布的,而我实现的时候是按照蓝色线圈内去分布刻度的。这样刻度分布不会非常密集,而且从实现上更简单。
android自定义View-HopPicker腕表_第2张图片

如何确定蓝色区域圆的圆心和半径呢?

1.以实心圆的圆心为坐标轴原点
2.设实心圆半径为 r ,设红色圆圈半径为R,有刻度的圆的半径为arcR。
3.大圆和红色圆的交点是(-R,0)和(R,0),大圆过实心圆直径3/4处,交点为(0,-r/2),这些交点是我根据作者原理图大致定的。(你也可以定义这些点的位置)
4.已知r,R,三个交点,求arcR的值和带有刻度圆的圆心位置。(求好之后才能绘图)

arcR和圆心求解过程:

android自定义View-HopPicker腕表_第3张图片

得到公式

求解得到acrR即可。

整个自定义view的代码如下:

public class HopPickerWatchView2 extends View {
    //控件实际的宽高,在onmeasure种获取
    private int viewWidth = 0;
    private int viewHeight = 0;
    private Paint mPaint = new Paint();
    //外圆画笔
    private Paint outPaint = new Paint();
    //刻度数,每一个刻度表示10min
    private int Scale = 72;
    //每一个刻度所占的角度
    private float degree = 360f / Scale;
    //旋转角度
    private float RotateDegree = 0;
    //每分钟度数 转一圈是12小时
    // 360 / (12*60) = 0.5
    private float perMinDegree = 0.5f;
    //以下7个属性的实际值还要在onmeasure重新测量
    //表盘半径
    private float r = 300;
    //外圆半径
    private float R = 350;
    //刻度圆半径  假定那个圆弧 过外圆半圆处点和表盘直径3/4处点 推算得出
    private float arcR = R * R / r + r / 4;
    //一小时间隔刻度线的长度
    private float hourLen = 60;
    //半小时间隔刻度线的长度
    private float halfHourLen = 45;
    //10min间隔刻度线的长度
    private float tenMinLen = 15;
    //刻度数字位置,在一小时刻度的下方
    private float numPosition = hourLen + 40;

    //watchface color
    private int DialColor = 0xFFE5E6EB;
    //black
    private int Black = 0xFF000000;
    //刻度圆路径
    private Path arcPath;//= new Path();
    //要扣的表盘
    private Path watchface;//= new Path();
    //刻度数字
    private String nums[] = {"12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"};
    // 抠图抗锯齿
    private PaintFlagsDrawFilter pfdf = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

    private void initMy() {//初始化画笔
        mPaint.setAntiAlias(true);
        mPaint.setColor(DialColor);
        outPaint.setAntiAlias(true);
        outPaint.setStyle(Paint.Style.STROKE);
    }

    private void initOnMeasure() {//跟屏幕尺寸相关的初始化操作放到这个函数里
        //初始化一些基本尺寸
        //  BoundsRadius = Math.min(viewWidth, viewHeight) / 2;
        r = Math.min(viewWidth, viewHeight) / 2;
        R = Math.min(viewWidth, viewHeight) / 2 + 50;
        arcR = R * R / r + r / 4;
        //初始化和尺寸相关的其他参数
        // TODO: 2016/10/1  画笔宽度和刻度长度最好由dp换算,这样不同dpi的手机看到的指针和表盘线条粗细是一样的
        outPaint.setStrokeWidth(5);
        outPaint.setTextSize(50);
        hourLen = 60;
        halfHourLen = 45;
        tenMinLen = 15;
        numPosition = hourLen +40;

        //onMeasure方法会被调用好几次,所以path.add方法 会 一直放入好几个 path
        //每次 直接new一个新的在add,就不会出现绘图的时候出现很多半径不同的圆了
        arcPath = new Path();
        arcPath.addCircle(0f, arcR - r / 2, arcR, Path.Direction.CW);
        watchface = new Path();
        watchface.addCircle(0f, 0f, r, Path.Direction.CW);
    }

    public HopPickerWatchView2(Context context, AttributeSet attrs) {
        super(context, attrs);
        initMy();
    }

    public HopPickerWatchView2(Context context) {
        super(context);
        initMy();
    }

    public HopPickerWatchView2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initMy();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //初始化坐标系,以view中心点为坐标原点,方便后续计算
        canvas.translate(viewWidth / 2, viewHeight / 2);
        //先画表盘
        canvas.drawCircle(0, 0, r, mPaint);
        //设置抗锯齿,不设置,扣出来的圆有明显锯齿
        canvas.setDrawFilter(pfdf);
        //抠圆
        canvas.clipPath(watchface);
        //开始旋转,每次旋转的角度由时间决定
        setRotateDegree();
        canvas.rotate(RotateDegree);
        //画刻度圆的画笔设置成黑色
        outPaint.setColor(Black);
        //绘制刻度圆
        canvas.drawPath(arcPath, outPaint);
        //绘制刻度
        canvas.save();
        //将坐标原点移动到刻度圆的圆心处
        canvas.translate(0, arcR - r / 2);
        //反向旋转刻度圆,使其一直保持相对水平
        canvas.rotate(-RotateDegree);
        for (int i = 0; i < Scale; i++) {
            if (i % 6 == 0) {//带数字的大刻度
                canvas.drawLine(0, arcR, 0, arcR - hourLen, outPaint);
                //开始绘制数字
                canvas.save();
                //位移画布,是数字绘制在刻度圆内,与刻度的距离可自行定义
                canvas.translate(0, numPosition - arcR);
                //纠正 数字旋转的角度 使表盘上的数字一直是正对我们的
                canvas.rotate(-i / 6 * 30);
                //绘制数字,并保证数字正对刻度线
                canvas.drawText(nums[i / 6], -outPaint.measureText(nums[i / 6]) / 2, 0, outPaint);
                canvas.restore();
            } else if (i % 6 == 3) {//间隔半小时的中刻度
                canvas.drawLine(0, arcR, 0, arcR - halfHourLen, outPaint);
            } else {//间隔10min的小刻度
                canvas.drawLine(0, arcR, 0, arcR - tenMinLen, outPaint);
            }
            //旋转 加偏移量,绘制下一条刻度线
            canvas.rotate(degree);
        }
        canvas.restore();

        //最后绘制指针 #F87219橘黄色
        outPaint.setColor(0xFFF87219);
        canvas.drawLine(0, -r, 0, r, outPaint);
        outPaint.setColor(Black);//恢复黑色
        postInvalidateDelayed(1000);//每秒刷新一次
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      //    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewWidth = MeasureSpec.getSize(widthMeasureSpec);
        viewHeight = MeasureSpec.getSize(heightMeasureSpec);
        initOnMeasure();

        setMeasuredDimension(viewWidth, viewHeight);
    }

    //根据当前时间设置画布旋转角度
    private void setRotateDegree() {
        Calendar calendar = Calendar.getInstance();
        int Hour = calendar.get(Calendar.HOUR);
        int Min = calendar.get(Calendar.MINUTE);
        RotateDegree = (Hour * 60 + Min) * perMinDegree;
    }
}
本文地址: http://blog.csdn.net/qq_24505485/article/details/52717381
源码地址: https://github.com/jhd147350/HopPickerWatch

补充:实际运行下来,看不到转动的效果,过一分钟才能看到指针转了一点点,效果图,都是快放的。

你可能感兴趣的:(android自定义view)