Android自定义View 闹钟唤起播放闹钟铃声实现

先上图看一下闹钟唤期页面的效果



实现的功能:

1:转动的图片根据天气情况更换

2:转动时间可以设置,转动结束,闹铃声音就结束

3:光圈颜色渐变效果


直接上代码啦:

package com.example.sirius.extension.customview;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.yuekong.sirius.extension.R;
import com.yuekong.sirius.extension.util.ExtendUtil;

import java.text.SimpleDateFormat;

/**
 * Created by Zhongqi.Shao on 2016/12/5.
 */
public class ClockWakeView extends View {

    //最外一层圆的颜色
    private int mOutCircleColor;
    //最外一层圆的半径
    private int mOutCircleRadis;
    //内圆的颜色
    private int mInnerCircleColor;
    //内圆的半径
    private int mInnerCircleRadis;

    private int mWidth;
    private int mHeight;

    //默认宽度
    private final int DEFAULT_WIDTH = dp2px(240);
    //默认高度
    private final int DEFAULT_HEIGHT = dp2px(240);
    //最外圆的默认半径
    private int DEFAULT_OUT_RADIS = dp2px(120);
    //内圆的默认半径
    private int DeFAULT_INNER_RADIS = dp2px(105);
    //标题距离顶部的默认距离
    private int DEFAULT_TITLE_PADDING_TOP = dp2px(40);

    //最外层圆形的Paint
    private Paint mOutCirclePaint;
    //内圆的Paint
    private Paint mInnerCirclePaint;
    //标题Paint
    private Paint mTitlePaint;
    //渐变弧
    private Paint mGradientArcPaint;
    private SweepGradient mSweepGradient;
    //日期时间Paint
    private Paint mTimePaint;
    private Paint mDatePaint;

    //时间分隔的图片
    private Bitmap mDividerPic;
    //天气图片
    private Bitmap mWeatherPic;
    //标题
    private String mTitle;

    //当前时间
    private long mCurrentDate = 1481010829605L;
    private SimpleDateFormat mFormat;
    private Ringtone mRingTone;

    //图片在倒计时几分钟内走完一个周期 根据音乐时间来计算
    private long mCountDownTime;
    //倒计时转过的角度
    private float mCurrentAngle;
    //倒计时结束回调
    private CountdownFinishListener mCountdownListener;

    private Context mContext;

    public ClockWakeView(Context context) {
        this(context, null);
        mContext = context;
    }

    public ClockWakeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        mContext = context;
    }

    public ClockWakeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SiriusClockWakeView);
        mOutCircleColor = ta.getColor(R.styleable.SiriusClockWakeView_outColor, context.getResources().getColor(R.color.out_circle_color));
        mOutCircleRadis = (int) ta.getDimension(R.styleable.SiriusClockWakeView_outCircleRadis, DEFAULT_OUT_RADIS);
        mInnerCircleColor = context.getResources().getColor(R.color.inner_circle_color);
        mInnerCircleRadis = DeFAULT_INNER_RADIS;
        mTitle = context.getResources().getString(R.string.clock);
        ta.recycle();
        initPaint();
    }

    private void initPaint() {
        mFormat = new SimpleDateFormat("MM月dd日_HH_mm_EEEE");
        //获取分隔图片
        mDividerPic = BitmapFactory.decodeResource(getResources(), R.drawable.time_divider);
        mWeatherPic = BitmapFactory.decodeResource(getResources(), R.drawable.icon_weather);
        //外圆的paint
        mOutCirclePaint = new Paint();
        mOutCirclePaint.setAntiAlias(true);
        mOutCirclePaint.setStyle(Paint.Style.STROKE);
        mOutCirclePaint.setStrokeWidth(dp2px(5));
        mOutCirclePaint.setColor(mOutCircleColor);

        //内圆的Paint
        mInnerCirclePaint = new Paint();
        mInnerCirclePaint.setAntiAlias(true);
        mInnerCirclePaint.setStyle(Paint.Style.STROKE);
        mInnerCirclePaint.setStrokeWidth(dp2px(5));
        mInnerCirclePaint.setColor(mInnerCircleColor);

        //标题的Paint
        mTitlePaint = new Paint();
        mTitlePaint.setAntiAlias(true);
        mTitlePaint.setStyle(Paint.Style.FILL);
        mTitlePaint.setColor(mContext.getResources().getColor(R.color.nav_highlighted));
        mTitlePaint.setTextSize(sp2px(14));

        //时间的Paint
        mTimePaint = new Paint();
        mTimePaint.setAntiAlias(true);
        mTimePaint.setColor(Color.WHITE);
        mTimePaint.setStyle(Paint.Style.FILL);
        mTimePaint.setTextSize(sp2px(70));

        //日期的Paint
        mDatePaint = new Paint();
        mDatePaint.setAntiAlias(true);
        mDatePaint.setColor(Color.WHITE);
        mDatePaint.setStyle(Paint.Style.FILL);
        mDatePaint.setTextSize(sp2px(14));

        //渐变弧的Paint
        mGradientArcPaint = new Paint();
        mGradientArcPaint.setAntiAlias(true);
        mGradientArcPaint.setStyle(Paint.Style.STROKE);
        mGradientArcPaint.setStrokeWidth(dp2px(5));
        mGradientArcPaint.setStrokeCap(Paint.Cap.BUTT);
        mGradientArcPaint.setStrokeJoin(Paint.Join.MITER);
        //设置渐变的颜色
        int[] colors = {0x00AEA1FF, 0x40AEA1FF, 0xFFAEA1FF, 0xFFAEA1FF};
        //渐变色
        mSweepGradient = new SweepGradient(0, 0, colors, null);
        mGradientArcPaint.setShader(mSweepGradient);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = startMeasure(widthMeasureSpec);
        mHeight = startMeasure(heightMeasureSpec);
        setMeasuredDimension(mWidth, mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //移动画布到中间
        canvas.translate(mWidth / 2, mHeight / 2);
        //绘制最外层的圆形
        drawOutCircle(canvas);
        //绘制内圆
        drawInnerCircle(canvas);
        //绘制中间的分隔图片
        drawDividerPic(canvas);
        //绘制标题
        drawTitle(canvas);
        //绘制时间
        drawDateTime(canvas);
        //绘制旋转图片
        drawLightArc(canvas);
    }

    private void drawOutCircle(Canvas canvas) {
        canvas.save();
        canvas.drawCircle(0, 0, mOutCircleRadis, mOutCirclePaint);
        canvas.restore();
    }

    private void drawInnerCircle(Canvas canvas) {
        canvas.save();
        canvas.drawCircle(0, 0, mInnerCircleRadis, mInnerCirclePaint);
        canvas.restore();
    }

    private void drawDividerPic(Canvas canvas) {
        canvas.save();
        canvas.drawBitmap(mDividerPic, null, new Rect(-dp2px(3), -dp2px(14), dp2px(3), dp2px(14)), null);
        canvas.restore();
    }

    private void drawTitle(Canvas canvas) {
        canvas.save();
        if (mTitle == null || mTitle.length() <= 0) {
            return;
        }
        float textWidth = mTitlePaint.measureText(mTitle);
        float baseLine = DeFAULT_INNER_RADIS - DEFAULT_TITLE_PADDING_TOP;
        canvas.drawText(mTitle, -textWidth / 2, -baseLine, mTitlePaint);
        canvas.restore();
    }


    private void drawLightArc(Canvas canvas) {
        canvas.save();
        canvas.rotate(mCurrentAngle);
        RectF rect = new RectF(-mInnerCircleRadis, -mInnerCircleRadis, mInnerCircleRadis, mInnerCircleRadis);
        canvas.drawArc(rect, -270, 180, false, mGradientArcPaint);
        canvas.drawBitmap(mWeatherPic, null, new Rect(-dp2px(12), -mInnerCircleRadis - dp2px(12), dp2px(12), dp2px(12) - mInnerCircleRadis), null);
        canvas.restore();
    }

    private void drawDateTime(Canvas canvas) {
        canvas.save();
        String timeStr = ExtendUtil.getAllFormatStr(mFormat, mCurrentDate);
        String[] array = timeStr.split("_");
        String date = array[0];
        String hour = array[1];
        String minute = array[2];
        String wake = array[3];

        float width = mTimePaint.measureText(hour);
        RectF targetRect = new RectF(-(width + dp2px(40)), -sp2px(70) / 2, -dp2px(40), sp2px(70) / 2);
        Paint.FontMetricsInt fontMetrics = mTimePaint.getFontMetricsInt();
        int baseline = (int) (targetRect.top + (targetRect.bottom - targetRect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top);
        canvas.drawText(hour, -width - dp2px(15), baseline, mTimePaint);

        float minuteWidth = mTimePaint.measureText(minute);
        RectF minuteRect = new RectF(dp2px(15), -sp2px(70) / 2, dp2px(15) + minuteWidth, sp2px(70) / 2);
        Paint.FontMetricsInt minuteMetrics = mTimePaint.getFontMetricsInt();
        int minuteLine = (int) (minuteRect.top + (minuteRect.bottom - minuteRect.top - minuteMetrics.bottom + minuteMetrics.top) / 2 - minuteMetrics.top);
        canvas.drawText(minute, dp2px(15), minuteLine, mTimePaint);

        String dateStr = date + "  " + wake;
        float dateWidth = mDatePaint.measureText(dateStr);
        RectF dateRect = new RectF(-dateWidth / 2, sp2px(30), dateWidth / 2, sp2px(30) + sp2px(14));
        Paint.FontMetricsInt dateMetrics = mTimePaint.getFontMetricsInt();
        int dateLine = (int) (dateRect.top + (dateRect.bottom - dateRect.top - dateMetrics.bottom + dateMetrics.top) / 2 - dateMetrics.top);
        canvas.drawText(dateStr, -dateWidth / 2, dateLine, mDatePaint);

        canvas.restore();
    }


    private int startMeasure(int whSpec) {
        int result = 0;
        int size = MeasureSpec.getSize(whSpec);
        int mode = MeasureSpec.getMode(whSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = DEFAULT_WIDTH;
        }
        return result;
    }

    //开始倒计时计算闹钟结束
    public void startCountDown() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1.0f);
        animator.setDuration(mCountDownTime);
        //匀速
        animator.setInterpolator(new LinearInterpolator());
        //不循环
        animator.setRepeatCount(0);
        //监听动画过程中值得实时变化
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mCurrentAngle = (float) valueAnimator.getAnimatedValue() * 360;
                invalidate();
            }
        });

        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {
                if (mRingTone != null && mRingTone.isPlaying()) {
                    mRingTone.stop();
                }
                if (mCountdownListener != null) {
                    mCountdownListener.countdownFinished();
                }
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        animator.start();
        //播放闹钟铃声
        Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
        mRingTone = RingtoneManager.getRingtone(mContext, notification);
        if (mRingTone != null && !mRingTone.isPlaying()) {
            mRingTone.play();
        }
    }

    //提供设置倒计时的方法
    public void setCountDownTime(long time, CountdownFinishListener listener) {
        mCountDownTime = time;
        mCountdownListener = listener;
    }

    //设置当前日期时间
    public void setCurrentTime(long currentTime) {
        mCurrentDate = currentTime;
        invalidate();
    }


    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }

    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

    public interface CountdownFinishListener {
        //倒计时结束操作
        void countdownFinished();
    }

}





评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值