美文网首页具体自定义控件UI效果仿写
自定义View_自定义时钟ClockView

自定义View_自定义时钟ClockView

作者: BraveJoy | 来源:发表于2019-09-14 10:15 被阅读0次

    前段时间一直忙着学理财给自己充电,有一段时间没写代码了,前两天做了一个梦,梦见自己在写代码,是做了一个自定义时钟的一个东西,然后我醒来后就想着把这个效果实现以下,也算是“梦”想成真了。先来看下效果图吧~

    OK,话不多说,直接安排~

    1.初始化画笔

    定义好需要的画笔,这里我们先定义4个,分别是绘制背景、基本元素、指针、数字四个Paint画笔。然后初始化进行画笔的基本设置。

    /**
     * 背景绘制画笔
     */
    private Paint mPaintBg;
    /**
     * 时钟元素画笔
     */
    private Paint mPaint;
    /**
     * 绘制指针的画笔
     */
    private Paint mPaintLine;
    /**
     * 绘制数字的画笔
     */
    private Paint mPaintText;
    
    //------------------------------------------------------------------------------
    
    /**
     * 初始化
     */
    private void initView() {
        mPaintBg = new Paint();
        mPaintBg.setAntiAlias(true);
        mPaintBg.setColor(Color.BLACK);
        mPaintBg.setStrokeWidth(dip2px(mContext, 2));
        mPaintBg.setStyle(Paint.Style.STROKE);
    
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.FILL);
    
        mPaintLine = new Paint();
        mPaintLine.setAntiAlias(true);
        mPaintLine.setStyle(Paint.Style.FILL);
        mPaintLine.setStrokeWidth(dip2px(mContext, 2));
    
        mPaintText = new Paint();
        mPaintText.setAntiAlias(true);
        mPaintText.setColor(Color.BLACK);
        mPaintText.setStyle(Paint.Style.STROKE);
        mPaintText.setTextSize(30);
    }
    
    
    2.初始化变量

    这里要定义三个变量来控制三个指针的长度,也用于后面计算坐标。

    /**
     * 指针长度-时针
     */
    private float mDistanceHour;
    /**
     * 指针长度-分针
     */
    private float mDistanceMinute;
    /**
     * 指针长度-秒针
     */
    private float mDistanceSecond;
    
    //------------------------------------------------------------------------------
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mDistanceHour = w / 5 - dip2px(mContext, 7);
        mDistanceMinute = mDistanceHour + dip2px(mContext, 18);
        mDistanceSecond = mDistanceHour + dip2px(mContext, 45);
    }
    
    
    3.定义相关变量

    这里我对时分秒分别定义了变量进行控制处理,主要是用来根据当前时间来计算指针所需要划过的角度。

    /**
     * 0-11
     */
    private int mCurrentHour = 0;
    /**
     * 0-59
     */
    private int mCurrentMinute = 0;
    /**
     * 0-59
     */
    private int mCurrentSecond = 0;
    

    OK,接下来上核心代码,也就是绘制部分。为了方便计算,我们把画笔移动到正中央,也就是先调用一下canvas.translate(getWidth() / 2, getHeight() / 2);然后再进行绘制。

    4.绘制时钟基本元素

    /**
     * 绘制时钟基本的元素
     *
     * @param canvas
     */
    private void drawClock(Canvas canvas) {
        //绘制圆圈背景
        canvas.drawCircle(0, 0, (getWidth() - dip2px(mContext, 2)) / 2, mPaintBg);
        //绘制文字和小圆点
        for (int i = 0; i < 12; i++) {
            float r = getWidth() / 2 - offset;
            float deg = i * 30 + 30;
            float x = (float) (r * Math.sin(Math.PI * deg / 180));
            float y = (float) (r * Math.cos(Math.PI * deg / 180));
            canvas.drawCircle(x, -y, dip2px(mContext, 3), mPaint);
            //绘制进度数字
            String text = (i + 1) + "";
            //获取文字宽度
            float textWidth = mPaintText.measureText(text, 0, text.length());
            float dx = x - x / 10 - textWidth / 2;
            Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
            float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
            float baseLine = -(y - y / 10) + dy;
            canvas.drawText(text, dx, baseLine, mPaintText);
        }
    }
    
    

    到这里我们就能看到基本的样子已经出来了,如下图:


    5.绘制时针

    /**
     * 绘制时针
     *
     * @param canvas
     */
    private void drawHour(Canvas canvas) {
        mPaintLine.setColor(Color.BLACK);
        /**
         * 找到时间和弧度的关系,先计算出总的时间加在一起是多少个小时,
         * 然后根据小时数计算出在时钟上需要绘制的角度是多少,再求出坐标进行绘制
         */
        float hour = (float) mCurrentHour + (float) mCurrentMinute / 60 + (float) mCurrentSecond / 3600;
        float deg1 = hour * 30;
        Log.i(TAG, "度数deg1:" + hour);
        float x1 = (float) (mDistanceHour * Math.sin(Math.PI * deg1 / 180));
        float y1 = (float) (mDistanceHour * Math.cos(Math.PI * deg1 / 180));
        //canvas.drawCircle(x1, -y1, 10, mPaintLine);
        canvas.drawLine(0, 0, x1, -y1, mPaintLine);
    }
    
    
    6.绘制分针

    /**
     * 绘制分针
     *
     * @param canvas
     */
    private void drawMinute(Canvas canvas) {
        mPaintLine.setColor(Color.BLACK);
        /**
         * 找到时间和弧度的关系
         */
        float minute = (float) mCurrentMinute + (float) mCurrentSecond / 60;
        float deg1 = minute * 6;
        Log.i(TAG, "===度数minute:" + minute + "===度数deg1:" + deg1);
        float x1 = (float) (mDistanceMinute * Math.sin(Math.PI * deg1 / 180));
        float y1 = (float) (mDistanceMinute * Math.cos(Math.PI * deg1 / 180));
        //canvas.drawCircle(x1, -y1, 10, mPaintLine);
        canvas.drawLine(0, 0, x1, -y1, mPaintLine);
    }
    
    7.绘制秒针

    /**
     * 绘制秒针
     *
     * @param canvas
     */
    private void drawSecond(Canvas canvas) {
        mPaintLine.setColor(Color.RED);
        /**
         * 找到时间和弧度的关系
         */
        float deg1 = mCurrentSecond * 6;
        Log.i(TAG, "===度数deg1:" + deg1);
        float x1 = (float) (mDistanceSecond * Math.sin(Math.PI * deg1 / 180));
        float y1 = (float) (mDistanceSecond * Math.cos(Math.PI * deg1 / 180));
        //canvas.drawCircle(x1, -y1, 10, mPaintLine);
        canvas.drawLine(0, 0, x1, -y1, mPaintLine);
    }
    
    8.动起来

    如何动起来,就很简单了,我们只要每隔1秒进行绘制一次即可,这里我使用了Handler+Runnable的方式进行实现的,代码很简单就不单独列出来了,下面直接上完整代码。

    ClockView.java

    /**
     * 自定义View系列-时钟Clock
     */
    public class ClockView extends View {
    
        private static final String TAG = ClockView.class.getSimpleName();
    
        /**
         * 上下文
         */
        private Context mContext;
        /**
         * 背景绘制画笔
         */
        private Paint mPaintBg;
        /**
         * 时钟元素画笔
         */
        private Paint mPaint;
        /**
         * 绘制指针的画笔
         */
        private Paint mPaintLine;
        /**
         * 绘制数字的画笔
         */
        private Paint mPaintText;
        /**
         * 0-11
         */
        private int mCurrentHour = 0;
        /**
         * 0-59
         */
        private int mCurrentMinute = 0;
        /**
         * 0-59
         */
        private int mCurrentSecond = 0;
    
        /**
         * 指针长度-时针
         */
        private float mDistanceHour;
        /**
         * 指针长度-分针
         */
        private float mDistanceMinute;
        /**
         * 指针长度-秒针
         */
        private float mDistanceSecond;
        /**
         * 偏移量
         */
        private float offset;
        /**
         * 时间间隔
         */
        private int TIME = 1000;//默认每隔10ms重绘一次
        /**
         * Handler
         */
        Handler handler = new Handler();
        /**
         * Runnable
         */
        Runnable runnable = new Runnable() {
    
            @Override
            public void run() {
                try {
                    handler.postDelayed(this, TIME);
                    switch (mTypeModel) {
                        case CURRENT_MODEL:
                            getCurrentTime();
                            break;
                        case DEFAULT_MODEL:
                            getDefaultTime();
                            break;
                    }
                    invalidate();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
    
        public ClockView(Context context) {
            this(context, null);
        }
    
        public ClockView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mContext = context;
            initView();
            offset = dip2px(mContext, 10);
        }
    
        /**
         * 初始化
         */
        private void initView() {
            mPaintBg = new Paint();
            mPaintBg.setAntiAlias(true);
            mPaintBg.setColor(Color.BLACK);
            mPaintBg.setStrokeWidth(dip2px(mContext, 2));
            mPaintBg.setStyle(Paint.Style.STROKE);
    
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.RED);
            mPaint.setStyle(Paint.Style.FILL);
    
            mPaintLine = new Paint();
            mPaintLine.setAntiAlias(true);
            mPaintLine.setStyle(Paint.Style.FILL);
            mPaintLine.setStrokeWidth(dip2px(mContext, 2));
    
            mPaintText = new Paint();
            mPaintText.setAntiAlias(true);
            mPaintText.setColor(Color.BLACK);
            mPaintText.setStyle(Paint.Style.STROKE);
            mPaintText.setTextSize(30);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mDistanceHour = w / 5 - dip2px(mContext, 7);
            mDistanceMinute = mDistanceHour + dip2px(mContext, 18);
            mDistanceSecond = mDistanceHour + dip2px(mContext, 45);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.translate(getWidth() / 2, getHeight() / 2);
            drawClock(canvas);
            drawHour(canvas);
            drawMinute(canvas);
            drawSecond(canvas);
    
            /**
             * 绘制中心点
             */
            canvas.drawCircle(0, 0, 20, mPaint);
        }
    
        /**
         * 绘制时钟基本的元素
         *
         * @param canvas
         */
        private void drawClock(Canvas canvas) {
            //绘制圆圈背景
            canvas.drawCircle(0, 0, (getWidth() - dip2px(mContext, 2)) / 2, mPaintBg);
            //绘制文字和小圆点
            for (int i = 0; i < 12; i++) {
    
                float r = getWidth() / 2 - offset;
                float deg = i * 30 + 30;
                float x = (float) (r * Math.sin(Math.PI * deg / 180));
                float y = (float) (r * Math.cos(Math.PI * deg / 180));
    
                canvas.drawCircle(x, -y, dip2px(mContext, 3), mPaint);
    
                //绘制进度数字
                String text = (i + 1) + "";
                //获取文字宽度
                float textWidth = mPaintText.measureText(text, 0, text.length());
                float dx = x - x / 10 - textWidth / 2;
                Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
                float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
                float baseLine = -(y - y / 10) + dy;
                canvas.drawText(text, dx, baseLine, mPaintText);
            }
        }
    
    
        /**
         * 绘制时针
         *
         * @param canvas
         */
        private void drawHour(Canvas canvas) {
            mPaintLine.setColor(Color.BLACK);
            /**
             * 找到时间和弧度的关系,先计算出总的时间加在一起是多少个小时,
             * 然后根据小时数计算出在时钟上需要绘制的角度是多少,再求出坐标进行绘制
             */
            float hour = (float) mCurrentHour + (float) mCurrentMinute / 60 + (float) mCurrentSecond / 3600;
            float deg1 = hour * 30;
            Log.i(TAG, "度数deg1:" + hour);
            float x1 = (float) (mDistanceHour * Math.sin(Math.PI * deg1 / 180));
            float y1 = (float) (mDistanceHour * Math.cos(Math.PI * deg1 / 180));
            //canvas.drawCircle(x1, -y1, 10, mPaintLine);
            canvas.drawLine(0, 0, x1, -y1, mPaintLine);
        }
    
    
        /**
         * 绘制分针
         *
         * @param canvas
         */
        private void drawMinute(Canvas canvas) {
            mPaintLine.setColor(Color.BLACK);
            /**
             * 找到时间和弧度的关系
             */
            float minute = (float) mCurrentMinute + (float) mCurrentSecond / 60;
            float deg1 = minute * 6;
            Log.i(TAG, "===度数minute:" + minute + "===度数deg1:" + deg1);
            float x1 = (float) (mDistanceMinute * Math.sin(Math.PI * deg1 / 180));
            float y1 = (float) (mDistanceMinute * Math.cos(Math.PI * deg1 / 180));
            //canvas.drawCircle(x1, -y1, 10, mPaintLine);
            canvas.drawLine(0, 0, x1, -y1, mPaintLine);
        }
    
        /**
         * 绘制秒针
         *
         * @param canvas
         */
        private void drawSecond(Canvas canvas) {
            mPaintLine.setColor(Color.RED);
            /**
             * 找到时间和弧度的关系
             */
            float deg1 = mCurrentSecond * 6;
            Log.i(TAG, "===度数deg1:" + deg1);
            float x1 = (float) (mDistanceSecond * Math.sin(Math.PI * deg1 / 180));
            float y1 = (float) (mDistanceSecond * Math.cos(Math.PI * deg1 / 180));
            //canvas.drawCircle(x1, -y1, 10, mPaintLine);
            canvas.drawLine(0, 0, x1, -y1, mPaintLine);
        }
    
    
        /**
         * 开始
         */
        public void start() {
            stop();
            handler.postDelayed(runnable, TIME); //每隔TIMEms执行
        }
    
        public void stop() {
            try {
                handler.removeCallbacks(runnable);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
        /**
         * 获取当前时间
         */
        private void getCurrentTime() {
            Calendar now = Calendar.getInstance();
            mCurrentHour = now.get(Calendar.HOUR_OF_DAY);
            mCurrentMinute = now.get(Calendar.MINUTE);
            mCurrentSecond = now.get(Calendar.SECOND);
        }
    
        private long timeSeconds = 32353450;
    
        private void getDefaultTime() {
            timeSeconds++;
            mCurrentHour = (int) (timeSeconds / 60 / 60 % 60);
            mCurrentMinute = (int) (timeSeconds / 60 % 60);
            mCurrentSecond = (int) (timeSeconds % 60);
        }
    
        private TYPE_MODEL mTypeModel = TYPE_MODEL.DEFAULT_MODEL;
    
        public enum TYPE_MODEL {
            CURRENT_MODEL, DEFAULT_MODEL;
        }
    
        public void startSpeed() {
            mTypeModel = TYPE_MODEL.DEFAULT_MODEL;
            TIME = 1;
        }
    
        public void defaultModel() {
            TIME = 1000;
            mTypeModel = TYPE_MODEL.CURRENT_MODEL;
        }
    
    
        /**
         * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
         */
        public static int dip2px(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
    }
    

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        private ClockView mClockView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mClockView = findViewById(R.id.view);
            mClockView.start();
        }
    
        /**
         * 加快版
         *
         * @param view
         */
        public void clickSpeed(View view) {
            mClockView.startSpeed();
        }
    
        /**
         * 当前时间
         *
         * @param view
         */
        public void clickDefault(View view) {
            mClockView.defaultModel();
        }
    
    }
    

    OK,结束,打完收工!

    相关文章

      网友评论

        本文标题:自定义View_自定义时钟ClockView

        本文链接:https://www.haomeiwen.com/subject/zrmiyctx.html