天天看點

Android 自定義View加屬性動畫實作動畫時鐘

目錄

前言

效果圖

項目實作

用到的屬性

重寫三個構造方法

初始化各個指針的畫筆,指針的矩形

重寫 onMeasure方法

擷取系統的時間

重寫ondraw方法

最後給時鐘添加上屬性動畫

前言

之前項目有個需求是在桌面上增加一個桌面時鐘,當時是參考别人的部落格,在其基礎上添加了屬性動畫然後實作的。是以今天特地重新寫了一遍這個自定義view 加深印象并分享出來

效果圖

用手機拍的視訊,然後轉為GIF的,因為是試用的,是以就是這個渣渣效果,還有水印,求推薦一個好用的格式轉換工具

Android 自定義View加屬性動畫實作動畫時鐘

項目實作

為了編寫友善,這裡沒有用到自定義屬性,有需要的話,可以自行添加自定義屬性,同時在代碼中注釋很詳細,文章這裡隻簡單介紹一下

用到的屬性

private Paint hPaint ,mPaint ,sPaint ,circlePaint ,cPointPaint,paintDegree,txtPaint,titlePaint;
    private final int maxWidth = 400;
    int width ,height;

    int hSound = 0;
    int mSound = 0;
    int sSound = 0;

    private int hcount = 0;//目前小時數
    private int mcount = 0;//目前分鐘數
    private int scount = 0;//目前秒鐘數

    private final int hScale = 30;//每小時之間30度
    private static final int mScale = 6;//每分鐘之間是6度

    RectF hRect , mRect ,sRect ;//三個
    boolean first = true;//執行動畫之後再轉動指針
           

重寫三個構造方法

public ClockView(Context context) {
        super(context);
        init();
    }

    public ClockView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
           

初始化各個指針的畫筆,指針的矩形

這裡各種new操作不要放在Ondraw方法中,避免重複實作

private void init(){
        //外圓盤畫筆
        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);
        circlePaint.setDither(true);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setColor(Color.BLACK);
        circlePaint.setStrokeWidth(1);

        //圓心畫筆
        cPointPaint = new Paint();
        cPointPaint.setAntiAlias(true);
        cPointPaint.setDither(true);
        cPointPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        cPointPaint.setColor(0xff4CAF50);//綠色
        cPointPaint.setStrokeWidth(2);

        //時針畫筆
        hPaint = new Paint();
        hPaint.setAntiAlias(true);
        hPaint.setDither(true);
        hPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        hPaint.setColor(0xff349CE2);//藍色
        hPaint.setStrokeWidth(7);
        //時針矩形
        hRect = new RectF(-16,0,89,0);

        //分針畫筆
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(0xffDB2540);//紅色
        mPaint.setStrokeWidth(6);
        //分針矩形
        mRect = new RectF(-25,0,140,0);

        //秒針畫筆
        sPaint = new Paint();
        sPaint.setAntiAlias(true);
        sPaint.setDither(true);
        sPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        sPaint.setColor(0xff4CAF50);//綠色
        sPaint.setStrokeWidth(5);
        //秒鐘矩形
        sRect = new RectF(-30,0,180,0);

        //刻度畫筆
        paintDegree  = new Paint();
        paintDegree.setColor(0xff000000);
        paintDegree.setStyle(Paint.Style.STROKE);
        paintDegree.setAntiAlias(true);

        //文字畫筆
        txtPaint = new Paint();
        txtPaint.setColor(0xff000000);
        txtPaint.setTextSize(20);
        txtPaint.setAntiAlias(true);

        //标題畫筆
        titlePaint = new Paint();
        titlePaint.setColor(0xff000000);
        titlePaint.setTextSize(45);
        titlePaint.setAntiAlias(true);

    }
           

重寫 onMeasure方法

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (widthMode == MeasureSpec.AT_MOST && heightMode ==MeasureSpec.AT_MOST){
            setMeasuredDimension(maxWidth,maxWidth);
        }else if (widthMode == MeasureSpec.AT_MOST ){
            setMeasuredDimension(maxWidth,width);
        }else if (heightMode ==MeasureSpec.AT_MOST ){
            setMeasuredDimension(height,maxWidth);
        }
    }
           

擷取系統的時間

private void getDatas() {
        SimpleDateFormat format = new SimpleDateFormat("HH,mm,ss");
        String time = format.format(new Date());
        try {
            String s[] = time.split(",");
            hcount = Integer.parseInt(s[0]);
            mcount = Integer.parseInt(s[1]);
            scount = Integer.parseInt(s[2]);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
           

重寫ondraw方法

首先擷取并計算寬高,設定各指針長度以及外圓的半徑 

super.onDraw(canvas);
        width = getMeasuredWidth()-getPaddingStart()-getPaddingEnd();
        height = getMeasuredHeight()-getPaddingBottom()-getPaddingTop();
        width = height = Math.min(width,height);
         //圓心點
        int cpoints = width/2;
        hSound = width/8;//時針長度
        mSound = width/6;//分針長度
        sSound = width/4;//秒針長度

        int radius =cpoints*3/4;//外圓半徑
           

借用一下别人的圖

Android 自定義View加屬性動畫實作動畫時鐘

這裡判斷是否是第一次顯示時鐘,等于true為第一次,然後繪制标題,位置的計算根據上圖

if(!first){
            getDatas();
        }

        String titleText = "三原色時鐘";
        //X坐标等于圓心的X坐标減去文字的一半的長度,Y坐标等于圓心的Y坐标減圓的半徑再減20
        canvas.drawText(titleText,cpoints - titlePaint.measureText(titleText) / 2 ,cpoints-radius-20,titlePaint);
           

然後繪制刻度,圓盤

//畫出12個小時的刻度線及文字
        for (int i = 0; i < 12; i++) {
            String txtTime = Integer.toString(i);
            //3,6,9,12比其他的略粗略長
            if(i%3==0) {
                if (i==0){
                    txtTime = "12";
                }
                paintDegree.setStrokeWidth(5);
                canvas.drawLine(width / 2, height / 2 - radius, width / 2, height / 2 - radius + 20, paintDegree);
            }else{
                paintDegree.setStrokeWidth(4);
                canvas.drawLine(width / 2, height / 2 - radius, width / 2, height / 2 - radius + 15, paintDegree);
            }
            canvas.drawText(txtTime,cpoints - txtPaint.measureText(txtTime) / 2,height / 2 - radius + 40,txtPaint);
            canvas.rotate(hScale, width / 2, height / 2);
        }

        //畫出60個分鐘的刻度線
        for (int x = 0; x < 60; x++) {
            paintDegree.setStrokeWidth(3);
            if (x % 5 != 0) {//當x % 5 == 0時即是時鐘刻度,是以不需要繪制,避免重複繪制
                canvas.drawLine(width / 2, height / 2 - radius, width / 2, height / 2 - radius + 8, paintDegree);
            }
            canvas.rotate(mScale, width / 2, height / 2);
        }

        //畫外層圓
        canvas.drawCircle(cpoints,cpoints,radius,circlePaint);
        //畫内層圓
        canvas.drawCircle(cpoints,cpoints,radius/6,circlePaint);
        //平移至中心點
        canvas.translate(cpoints,cpoints);
        //儲存畫布
        canvas.save();
           

最後是繪制三個指針

//int hRotate = 270 + hScale * hcount;
        int offset = 30 * mcount / 60;
        offset -= offset % mScale;//時針相對分針數,有一個偏移量
        int hRotate = 270 + hScale * hcount + offset;
        canvas.rotate(hRotate);
//        canvas.drawLine(0, -10, 0, hSound, hPaint);//畫時針

        canvas.drawRoundRect(hRect,15,15,hPaint);//畫時針

        canvas.restore();
        canvas.save();
        int mRotate = 270 + mScale * mcount ;
        canvas.rotate(mRotate);

        canvas.drawRoundRect(mRect,25,25,mPaint);//畫分針
        //canvas.drawLine(0, -15, 0, mSound, mPaint);//畫分針

        canvas.restore();
        canvas.save();
        //一圈360度,總共60秒,是以時間每多一秒,度數加6
        int sRotate = 270 + mScale * scount ;
        canvas.rotate(sRotate);
        //canvas.drawLine(0, -25, 0, sSound, sPaint);//畫秒針

        canvas.drawRoundRect(sRect,15,15,sPaint);//畫秒針
           

繪制指針上的圓心點

canvas.drawCircle(0,0,6,cPointPaint);//畫圓心
           

第一次出現時不轉動指針

if(!first){
            postInvalidateDelayed(1000);
        }
           

最後給時鐘添加上屬性動畫

在View中添加一個公共方法給外部調用

public void startAnim(){
        getDatas();
        final ValueAnimator animatorh = ValueAnimator.ofInt(0,hcount>12?hcount-12:hcount);//大于十二點時減去12 避免轉兩圈
        final ValueAnimator animatorm=ValueAnimator.ofInt(0,mcount);
        final ValueAnimator animators=ValueAnimator.ofInt(0,scount);

        //設定動畫時長
        animatorh.setDuration(1500);
        animatorm.setDuration(1500);
        animators.setDuration(1500);
        animatorh.setInterpolator(new LinearInterpolator());
        animatorh.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                hcount = (int)animation.getAnimatedValue();
            }
        });
        animatorh.start();

        animatorm.setInterpolator(new LinearInterpolator());
        animatorm.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mcount = (int)animation.getAnimatedValue();
            }
        });
        animatorm.start();

        animators.setInterpolator(new LinearInterpolator());
        animators.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                scount = (int)animation.getAnimatedValue();
                postInvalidate();//添加之後動畫才會執行,不然看不到效果
            }
        });
        animators.start();
        //添加動畫完成時的監聽,在動畫完成之後開始指針的轉動
        animators.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                first = false ;
                postInvalidate();
            }
        });
    }
           

有時不需要執行動畫的話,可以再添加一個公共方法直接設定first 為false

public void SetFirstInit(Boolean  ttt) {
        first =ttt;
    }
           

//然後在布局添加自定義view即可

Android 自定義View加屬性動畫實作動畫時鐘

在Activity中使用ClockView并根據需求調用對應的方法

Android 自定義View加屬性動畫實作動畫時鐘

到這裡文章就結束了,覺得還算可以的,歡迎點贊啊