Android 自定义View 之钟表

 由此starty 也可以推导出来

            float starty = (float) (mWidth / 2 - textRedius * Math.cos(Math.PI / 6 * i) + mTextPaint.measureText(times[i]+"") / 2);
        cos0度 = 1
        cos30度 = 0.8
        cos60度 = 0.5
        cos90度 = 0
        cos120度 = -0.5
        cos150度 = -0.8
        cos180度 = -1
        cos210度 = -0.8
        cos240度 = -0.5
        cos270度 = 0
        cos300度 = 0.5
        cos330度 = 0.8
        cos360度 = 1

也就是说向上为正 向下为负 和startx 差不多 模拟了一个 值的变化


 


public class MyClock extends View {


    // View 的默认宽高  实际上这里是画圆 所以宽高都是一样的
    private static final int DEAFAULT_WIDTH = 200;
    private static final int DEAFAULT_HEIGHT = 200;


    /**
     * 实际测量的宽高
     */
    private int mWidth;
    private int mHeight;
    //  表框的画笔 最外层大圆
    private Paint mWaicengPaint;
    private Paint mNeicengPaint;
    private Paint mShizhenPaint;
    private Paint mMiaozhenPaint;
    private Paint mFenzhenPaint;
    private Paint mTextPaint;
    private Paint mKeduPaint;

    // 数字
    private int[] times = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

    // 时
    private int shi;
    // 分
    private int fen;
    // 秒
    private int miao;


    //外层圆的宽度
    private float mWaicengyuanWidth = 2f;
    private Thread thread;

    public MyClock(Context context) {
        this(context, null);
    }

    public MyClock(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyClock(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaints();

    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (widthMode == MeasureSpec.EXACTLY) {
            mWidth = Math.min(mWidth, mHeight);
        } else {
            mWidth = dip2px(getContext(), DEAFAULT_WIDTH);
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            mHeight = Math.min(mWidth, mHeight);
        } else {
            mHeight = dip2px(getContext(), DEAFAULT_HEIGHT);
        }
        mWidth = mHeight = Math.min(mWidth, mHeight);
        setMeasuredDimension(mWidth, mHeight);

    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        Calendar calendar = Calendar.getInstance();
        shi = calendar.get(Calendar.HOUR);
        fen = calendar.get(Calendar.MINUTE);
        miao = calendar.get(Calendar.SECOND);

        //画外层圆
        drawWaiceng(canvas);

        //画刻度
        drawKedu(canvas);

        //写数
        drawText(canvas);


        //画时针
        drawShizhen(canvas);

        //画分针
        drawFenzhen(canvas);

        //画秒针
        drawMiaozhen(canvas);

        //画中心圆
        drawNeiceng(canvas);


    }

    private void drawMiaozhen(Canvas canvas) {
        //首先是半径  分为2部分 一个是长的部分  一个是短的部分
        //半径比时针大一点  比秒小一些
        float longRedius = mWidth / 2 - 80;
        float shortRedius = 60;

        double v = Math.PI / 30;  //因为分针是60个位置  时针12个 为5倍  所以这里除以30 时针除以6
        float startX = (float) (mWidth / 2 - shortRedius * Math.sin(miao * v));
        float startY = (float) (mWidth / 2 + shortRedius * Math.cos(miao * v));

        float endX = (float) (mWidth / 2 + longRedius * Math.sin(miao * v));
        float endY = (float) (mWidth / 2 - longRedius * Math.cos(miao * v));
        canvas.drawLine(startX, startY, endX, endY, mMiaozhenPaint);

    }


    private void drawFenzhen(Canvas canvas) {
        //首先是半径  分为2部分 一个是长的部分  一个是短的部分
        //半径比时针大一点  比秒小一些
        float longRedius = mWidth / 3;
        float shortRedius = 60;

        double v = Math.PI / 30;  //因为分针是60个位置  时针12个 为5倍  所以这里除以30 时针除以6
        float startX = (float) (mWidth / 2 - shortRedius * Math.sin(fen * v));
        float startY = (float) (mWidth / 2 + shortRedius * Math.cos(fen * v));

        float endX = (float) (mWidth / 2 + longRedius * Math.sin(fen * v));
        float endY = (float) (mWidth / 2 - longRedius * Math.cos(fen * v));
        canvas.drawLine(startX, startY, endX, endY, mFenzhenPaint);


    }


    private void drawShizhen(Canvas canvas) {
        //首先是半径  分为2部分 一个是长的部分  一个是短的部分
        float longRedius = mWidth / 4;
        float shortRedius = 60;

        double v = Math.PI / 6;
        float startX = (float) (mWidth / 2 - shortRedius * Math.sin(shi * v));
        float startY = (float) (mWidth / 2 + shortRedius * Math.cos(shi * v));

        float endX = (float) (mWidth / 2 + longRedius * Math.sin(shi * v));
        float endY = (float) (mWidth / 2 - longRedius * Math.cos(shi * v));
        canvas.drawLine(startX, startY, endX, endY, mShizhenPaint);
    }

    // 写数 方法1***********************************************
    private void drawText(Canvas canvas) {
        int textRedius = mWidth / 2 - 50;//文字构成的圆的半径
        for (int i = 0; i < 12; i++) {
            // Math.PI  就是 π 3.1415926    实际上他就是180°
            float startX = (float) (mWidth / 2 + textRedius * Math.sin(Math.PI / 6 * i) - mTextPaint.measureText(times[i] + "") / 2);
            float starty = (float) (mWidth / 2 - textRedius * Math.cos(Math.PI / 6 * i) + mTextPaint.measureText(times[i] + "") / 2);
            canvas.drawText(times[i] + "", startX, starty, mTextPaint);
        }

    }


    private void drawNeiceng(Canvas canvas) {
        canvas.drawCircle(mWidth / 2, mWidth / 2, 10, mNeicengPaint);
    }


    private void drawKedu(Canvas canvas) {
        //因为刻度需要旋转画布 首先要保存现在的状态  画完之后 恢复状态
        canvas.save();

        //刻度的长度
        float mKeduLeng;

        int a = 0;
        // 0-60  60 个刻度
        for (int i = 0; i < 60; i++) {
            //每5个会有一个较粗
            if (i % 5 == 0) {
                mKeduPaint.setStrokeWidth(5);
                mKeduLeng = 20f;

                // 写数 方法2***********************************************
                //刻度最大的20  处写数  40是自己定的间距
//                String text = times[a]+"";
//                a++;
//                canvas.drawText(text, mWidth/2-(mTextPaint.measureText(text)/2), 20+mWaicengyuanWidth+40, mTextPaint);

            } else {
                mKeduPaint.setStrokeWidth(2);
                mKeduLeng = 10f;
            }
            canvas.drawLine(mWidth / 2.f, mWaicengyuanWidth, mWidth / 2.f,
                    mWaicengyuanWidth + mKeduLeng, mKeduPaint);
            // 每画一次就旋转360/60 度
            canvas.rotate(360 / 60f, mWidth / 2, mHeight / 2);
        }

        // 恢复状态
        canvas.restore();


    }


    private void drawWaiceng(Canvas canvas) {
        canvas.drawCircle(mWidth / 2.f, mHeight / 2.f, mWidth / 2, mWaicengPaint);
    }


    /**
     * 根据手机的分辨率从 dip 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }


    /**
     * 初始化所有用到的画笔
     */
    private void initPaints() {
        //画最外层大圆的画笔
        // 黑色
        mWaicengPaint = new Paint();
        mWaicengPaint.setAntiAlias(true);//抗锯齿
        mWaicengPaint.setDither(true);//防抖动
        mWaicengPaint.setColor(Color.BLACK);
        mWaicengPaint.setStyle(Paint.Style.STROKE);
        mWaicengPaint.setStrokeWidth(mWaicengyuanWidth);


        //画刻度的画笔
        // 黑色
        mKeduPaint = new Paint();
        mKeduPaint.setAntiAlias(true);//抗锯齿
        mKeduPaint.setDither(true);//防抖动
        mKeduPaint.setColor(Color.BLACK);
        mKeduPaint.setStyle(Paint.Style.STROKE);
        mKeduPaint.setStrokeWidth(2);  //这个会被修改


        //画内层小圆的画笔
        // 黑色
        mNeicengPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mNeicengPaint.setDither(true);//防抖动
        mNeicengPaint.setColor(Color.BLACK);
        mNeicengPaint.setStyle(Paint.Style.FILL);


        //画时针的画笔
        // 红色
        mShizhenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mShizhenPaint.setDither(true);//防抖动
        mShizhenPaint.setColor(Color.RED);
        mShizhenPaint.setStyle(Paint.Style.STROKE);
        mShizhenPaint.setStrokeWidth(10);

        //画分针的画笔
        // 橙色
        mFenzhenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mFenzhenPaint.setDither(true);//防抖动
        mFenzhenPaint.setColor(Color.BLUE);
        mFenzhenPaint.setStyle(Paint.Style.STROKE);
        mFenzhenPaint.setStrokeWidth(6);

        //画秒针的画笔
        // 黄色
        mMiaozhenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mMiaozhenPaint.setDither(true);//防抖动
        mMiaozhenPaint.setColor(Color.BLACK);
        mMiaozhenPaint.setStyle(Paint.Style.STROKE);
        mMiaozhenPaint.setStrokeWidth(2);


        //画数字的画笔
        // 黑色
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        mTextPaint.setDither(true);//防抖动
        mTextPaint.setColor(Color.BLACK);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize(40);

    }


    private boolean canChange = true;
    Handler mHandler = new Handler() {
        @SuppressLint("HandlerLeak")
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            int what = msg.what;
            if (what == 0) {
                if (canChange) {
                    invalidate();
                } else {

                }

            }

        }
    };

    private boolean isStart = false;

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        thread = new Thread() {
            @Override
            public void run() {
                super.run();
                while (true) {
                    try {
                        Thread.sleep(1000);
                        mHandler.sendEmptyMessage(0);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        };


    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mHandler.removeCallbacksAndMessages(null);
        //中断线程
        thread.interrupt();
    }


    public void start() {
        if (isStart) {
            setCanChange(true);
        } else {
            thread.start();
            isStart = true;
        }

    }

    public void stop() {
        setCanChange(false);
    }

    public void setCanChange(boolean canChange) {
        this.canChange = canChange;
    }
}

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final MyClock mClock = findViewById(R.id.mClock);
        Button butStart = findViewById(R.id.butStart);
        Button butEnd = findViewById(R.id.butEnd);

        butStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mClock.start();
            }
        });

        butEnd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mClock.stop();
            }
        });



    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <com.as.demo_ok69_clock.MyClock
        android:id="@+id/mClock"
        android:layout_width="match_parent"
        android:layout_height="300dp" />

    <Button
        android:id="@+id/butStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="开始" />

    <Button
        android:id="@+id/butEnd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="暂停" />

</LinearLayout>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值