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

目錄

 

前言

效果圖

項目實現

用到的屬性

重寫三個構造方法

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

重寫 onMeasure方法

獲取系統的時間

重寫ondraw方法

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


前言

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

效果圖

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

項目實現

爲了編寫方便,這裏沒有用到自定義屬性,有需要的話,可以自行添加自定義屬性,同時在代碼中註釋很詳細,文章這裏只簡單介紹一下

用到的屬性

 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;//外圓半徑
 

借用一下別人的圖

手機座標圖

 

這裏判斷是否是第一次顯示時鐘,等於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即可

 

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

 

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章