Android自定义时钟控件

项目要求访问网络是等待状态要做时钟的样子,经过不懈努力,终于写出来了,现分享出来,功能比较全,直接拷贝代码就可以用,仅供有这种需求的码农们参考,如果采纳,请点个赞,谢谢支持。


效果图


主Activity,主要是在访问接口的时候开启时钟,接口访问结束关闭时钟。

package com.example.myapplication;

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

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

        final UFWaitingIndicatorView uFWaitingIndicatorView = (UFWaitingIndicatorView) findViewById(R.id.uFWaitingIndicatorView);
         
        findViewById(R.id.open).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                uFWaitingIndicatorView.setTintColor(Color.WHITE);
                uFWaitingIndicatorView.start();//时钟开始绘制
            }
        });

        findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                uFWaitingIndicatorView.stopAnimated(false);//停止绘制时钟
            }
        });

    }
}


自定义时钟

package com.cccollector.magazine.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.cccollector.magazine.R;

import java.util.Timer;
import java.util.TimerTask;


/**
 * 仿小米时钟
 */
public class UFWaitingIndicatorView extends View {

    private float hourHandStartAngle;//时针当前角度
    private float minuteHandStartAngle;//分针当前角度;
    private float stopAnimateDuration; // 停止动画时长
    private float stopAnimateProgress; // 停止动画进度,
    private boolean hiddenWhenStop;//执行完之后是否隐藏;
    private float minuteHandDuration;//分针一次旋转多少度;
    private float speedRatio;//时针是分针的几倍速度;
    private float lineWidth;//时针,分针的线段宽度;
    private float hourHandCurrentAngle;//时针当前角度;;
    private float minuteHandCurrentAngle;//分针当前角度;
    private int refreshDuration;//定时器多久刷新一次;
    private Paint paint;//画笔;
    private float mRadius;//时钟半径,不包括padding值;
    private Canvas mCanvas;//画布;
    private Timer waitingTimer;//定时器;
    private Timer stopTimer;


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

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

    public UFWaitingIndicatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //配置文件读出默认配置,配置的属性可以写到xml中生效;
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UFWaitingIndicatorView);
        float hourHandStartAngle_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_hourHandStartAngle, 45);
        float minuteHandStartAngle_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_minuteHandStartAngle, 315);
        float minuteHandDuration_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_minuteHandDuration, 2000);
        float speedRatio_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_speedRatio, 0.375f);
        float lineWidth_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_lineWidth, 2);
        paint = new Paint();//画笔;
        hourHandStartAngle = 0;
        minuteHandStartAngle = 0;
        hiddenWhenStop = false;
        minuteHandDuration = minuteHandDuration_attrs;
        speedRatio = speedRatio_attrs;
        lineWidth = lineWidth_attrs;
        hourHandCurrentAngle = 0;
        minuteHandCurrentAngle = 0;
        refreshDuration = 50;
        stopAnimateDuration = 1000;
        stopAnimateProgress = 1000;
        setTintColor(Color.parseColor("#15B300"));
    }


    //重写测量方法;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureDimension(widthMeasureSpec), measureDimension(heightMeasureSpec));
    }

    private int measureDimension(int measureSpec) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = 260;
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

    /**
     * 重写方法得到控件的宽,即圆的半径;
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //宽和高分别去掉padding值,其中最小值为半径值
        mRadius = (float)Math.min(Math.floor(w /2) , Math.floor(h / 2)) - 3;
    }

    /**
     * 绘制
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        //画布;
        mCanvas = canvas;
        //线段的宽;
        paint.setStrokeWidth(dip2px(getContext(),lineWidth));
        mCanvas.save();//保存画布;
        mCanvas.rotate(hourHandCurrentAngle, getWidth() / 2, getHeight() / 2);//以画布中心为圆点,旋转画布;
        mCanvas.translate(getWidth() / 2, getHeight() / 2);
//        mCanvas.drawPoint(0,0,paint);
        mCanvas.drawLine(0f,0f,-(float) (mRadius / Math.sqrt(2)),-(float) (mRadius / Math.sqrt(2)),paint);//画时针;
        mCanvas.restore();//复位画布;

        mCanvas.save();//保存画布;
        mCanvas.rotate(minuteHandCurrentAngle, getWidth() / 2, getHeight() / 2);//以画布中心为圆点,旋转画布;
        mCanvas.translate(getWidth() / 2, getHeight() / 2);
//        mCanvas.drawPoint(0,0,paint);
        mCanvas.drawLine(0f,0f,-(float) (mRadius / Math.sqrt(2)),(float) (mRadius / Math.sqrt(2)),paint);//画分针;
        mCanvas.restore();//复位画布;
    }

    /**
     * 设置指针颜色;
     * @param color
     */
    public void setTintColor (int color) {
        paint.reset();//让画笔的设置回到原始状态;
        paint.setStrokeCap(Paint.Cap.ROUND);//设置成圆头;
        //抗锯齿
        paint.setAntiAlias(true);
        paint.setColor(color);
        //刷新(onDraw)调用重绘方法;
        invalidate();
    }

    /**
     * 设置背景颜色;
     * @param color
     */
    public void setBackgroundColor (int color) {
        setBackgroundColor(color);
    }

    /**
     * 设置线宽;
     * @param mLineWidth
     */
    public void setLineWidth (float mLineWidth) {
        this.lineWidth = mLineWidth;
    }

    /**
     * dp转换成px;
     * @param context
     * @param dpValue
     * @return
     */
    public  int dip2px(Context context, float dpValue){
        final float scale=context.getResources().getDisplayMetrics().density;
        return (int)(dpValue*scale+0.5f);
    }

    /**
     * 如果计时器不为空,则说明正在进行,如果为空,,没进行;
     * @return
     */
    public  boolean started(){
        if (waitingTimer != null) {
            return true;
        }else {
            return false;
        }
    }

    public boolean stoping () {
        return waitingTimer != null && stopAnimateProgress < stopAnimateDuration;
    }

    /**
     * 开始动画;
     */
    public void start() {
        // 如果已开始,则返回
        if (started()) {
            return;
        }

        // 将起始角度设为当前角度
        hourHandCurrentAngle = this.hourHandStartAngle;
        minuteHandCurrentAngle = this.minuteHandStartAngle;

        //创建计时器;
        // 初始化定时器
        waitingTimer = new Timer();
        waitingTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                // 计算当前角度
                hourHandCurrentAngle += (refreshDuration / (minuteHandDuration / speedRatio)) * 360;
                minuteHandCurrentAngle += (refreshDuration / minuteHandDuration) * 360;
                mHandler.sendEmptyMessage(Timer_MESSAGE);

            }
        }, 0, refreshDuration);//第二个参数,延迟几秒开始第一次刷新,第三个参数,每隔几秒刷新;
    }

    private static final int Timer_MESSAGE =1000;
    private static final int StopTimer_MESSAGE =1001;
    //定时器配合handler实现,定时器子线程发送消息,handler主线程刷新界面;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case Timer_MESSAGE:
                    // 刷新显示
                    invalidate();
                    Log.i("TAG", "handleMessage: "+"ting");
                    break;
                case StopTimer_MESSAGE:

                    // 刷新显示
                    invalidate();
//                    if (hiddenWhenStop) {//执行完之后隐藏
//                        setVisibility(View.GONE);
//                    }
                    break;
            }
        }
    };

    /**
     * 停止动画;
     */
    public void stopAnimated(boolean animated) {
        if (started()) {
            //停止刷新等待计时器;。。。。。。。后面要重新开一个停止计时器;
            waitingTimer.cancel();
            waitingTimer = null;
        }

        if (animated) {

            // 缩减到360度以内
            hourHandCurrentAngle = (float) Math.floor(hourHandCurrentAngle - Math.floor(hourHandCurrentAngle / 360) * 360);
            minuteHandCurrentAngle =(float) Math.floor(minuteHandCurrentAngle - Math.floor(minuteHandCurrentAngle / 360) * 360);
            // 动画开始时的角度
            final float hourHandAnimateBeginAngle = hourHandCurrentAngle;
            final float minuteHandAnimateBeginAngle = minuteHandCurrentAngle;
            // 动画经过的的角度
            final double hourHandAnimateAngle = hourHandAnimateBeginAngle < this.hourHandStartAngle ? this.hourHandStartAngle - hourHandAnimateBeginAngle : this.hourHandStartAngle + 360 - hourHandAnimateBeginAngle;
            final double minuteHandAnimateAngle = minuteHandAnimateBeginAngle < this.minuteHandStartAngle ? this.minuteHandStartAngle - minuteHandAnimateBeginAngle : this.minuteHandStartAngle + 360 - minuteHandAnimateBeginAngle;

            // 初始化进度
            stopAnimateProgress = 0;
            //停止计时器;
            stopTimer = new Timer();
            stopTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    double t = stopAnimateProgress;
                    double d = stopAnimateDuration;//一秒之内停止进度;
//                double progress = -1 * (t /= d) * (t - 2);
//                double progress = (t = t / d - 1) * t * t + 1;
//                double progress = -((t = t / d - 1) * t * t * t - 1);
                    double progress = (t = t / d - 1) * t * t * t * t + 1;

                    // 计算当前角度
                    hourHandCurrentAngle = (float) (hourHandAnimateBeginAngle + progress * hourHandAnimateAngle);
                    minuteHandCurrentAngle = (float) (minuteHandAnimateBeginAngle + progress * minuteHandAnimateAngle);
                    // 刷新显示
//                    invalidate();
                    mHandler.sendEmptyMessage(Timer_MESSAGE);
                    // 更新进度
                    stopAnimateProgress += refreshDuration;
                    // 如果进度大于等于时长
                    if (stopAnimateProgress >= stopAnimateDuration) {
                        hourHandCurrentAngle = 0;
                        minuteHandCurrentAngle = 0;
                        // 刷新显示
//                        invalidate();
                        if (stopTimer != null) {
                            stopTimer.cancel();
                            stopTimer = null;
                        }
                        mHandler.sendEmptyMessage(StopTimer_MESSAGE);
                    }
                }
            },0,refreshDuration);



        }else {
            // 恢复初始设置
            hourHandCurrentAngle = 0;
            minuteHandCurrentAngle = 0;
            // 刷新显示
//            stopTimer.cancel();
//            stopTimer = null;
            mHandler.sendEmptyMessage(StopTimer_MESSAGE);
        }
    }


}

为了尽可能的不劳累小伙伴们,特把布局也贴上

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.myapplication.MainActivity">

    <Button
        android:id="@+id/open"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启时钟" />
    <Button
        android:id="@+id/close"
        android:layout_below="@id/open"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="关闭时钟" />

    <RelativeLayout
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="200dp"
        android:background="@drawable/shape_pointer">
        <com.example.myapplication.UFWaitingIndicatorView
            android:id="@+id/uFWaitingIndicatorView"
            android:layout_width="22dp"
            android:layout_height="22dp"
            android:layout_centerInParent="true"/>
    </RelativeLayout>
</RelativeLayout>


shape_pointer 时钟椭圆形状 ,可根据需求,自己定义

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners  android:radius="6dp"/>
    <solid android:color="#000000"/>
</shape>

attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="UFWaitingIndicatorView">
        <!--时针起始角度-->
        <attr name="hourHandStartAngle" format="dimension" />
        <!--分针起始角度-->
        <attr name="minuteHandStartAngle" format="dimension" />
        <!--分针一圈时长-->
        <attr name="minuteHandDuration" format="dimension" />
        <!--时针分针速度比(0~1)-->
        <attr name="speedRatio" format="dimension" />
        <!--线宽度-->
        <attr name="lineWidth" format="dimension" />
    </declare-styleable>
</resources>



到之处代码已经写完,直接拷贝到项目的对应目录就可以的,如果对你有帮助,请点赞评论,谢谢支持。












  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值