轉載請標明出處:
https://blog.csdn.net/m0_38074457/article/details/85790550,本文出自【陳少華的博客】
<declare-styleable name="AlarmClockView"> <attr name="outerCircleColor" format="reference|color" /> <attr name="innerCircleColor" format="reference|color" /> <attr name="secondHandColor" format="reference|color" /> <attr name="minuteHandColor" format="reference|color" /> <attr name="hourHandColor" format="reference|color" /> <attr name="minuteScaleColor" format="reference|color" /> <attr name="scaleColor" format="reference|color" /> <attr name="dateValueColor" format="reference|color" /> <attr name="isShowTime" format="boolean" /> <attr name="proportion" format="float" /> </declare-styleable>
1)構造方法中通過initView獲取初始化值。
2)onSizeChanged方法中獲取控件的寬高,並設置時鐘的中心點座標、半徑等信息。
3)onDraw方法中根據上圖繪製流程對時鐘進行繪製。
4)調用start方法並設置監聽啓動鬧鐘,通過handler每隔1秒獲取當前時間,並刷新控件。
import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.os.Build; import android.os.Handler; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.view.View; import java.util.Calendar; /** * Created by HARRY on 2019/1/4 0004. */ public class AlarmClockView extends View { /** * 秒針顏色 */ private int mSecondHandColor; /** * 分針顏色 */ private int mMinuteHandColor; /** * 時針顏色 */ private int mHourHandColor; /** * 分鐘刻度顏色 */ private int mMinuteScaleColor; /** * 當分鐘是5的倍數時刻度的顏色 */ private int mPointScaleColor; /** * 時鐘底部時間文本顏色 */ private int mDateValueColor; /** * 時鐘寬度 */ private int mClockWid; /** * 時鐘最外層圓半徑 */ private int mOuterRadius; /** * 時鐘圓心x */ private int mCenterX; /** * 時鐘圓心y */ private int mCenterY; /** * 控件寬 */ private int mWid; /** * 控件高 */ private int mHei; private Paint mPaint = new Paint(); /** * 最外層圓顏色 */ private int mOuterCircleColor; /** * 內層圓顏色 */ private int mInnerCircleColor; /** * 內層半徑 */ private int mInnerRadius; /** * 內外圓的間距 */ private int mSpace = 10; /** * 現在的時間小時 */ private int mHour; /** * 現在的時間分鐘 */ private int mMinute; /** * 現在的時間秒 */ private int mSecond; /** * 時鐘上刻度值的高度 */ private int mScaleValueHei; private String[] arr = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"}; /** * 現在的時間天 */ private int mDay; /** * 現在的時間周幾 */ private int mWeek; /** * 現在的時間月 */ private int mMonth; /** * 現在的時間年 */ private int mYear; /** * 是否顯示時鐘底部的時間文本 */ private boolean mIsShowTime; /** * 真實的周幾 */ private String mWeekStr; /** * 時間監聽 */ private TimeChangeListener listener; /** * 時鐘佔空間整體的比例 */ private float mProportion; /** * handler用來處理定時任務,沒隔一秒刷新一次 */ private Handler mHandler = new Handler(); private Runnable runnable = new Runnable() { @Override public void run() { mHandler.postDelayed(this, 1000); initCurrentTime(); } }; public AlarmClockView(Context context) { this(context, null); } public AlarmClockView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public AlarmClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context, attrs); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public AlarmClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initView(context, attrs); } private void initView(Context context, AttributeSet attrs) { TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.AlarmClockView); if (array != null) { mOuterCircleColor = array.getColor(R.styleable.AlarmClockView_outerCircleColor, getResources().getColor(R.color.gray)); mInnerCircleColor = array.getColor(R.styleable.AlarmClockView_innerCircleColor, getResources().getColor(R.color.grayInner)); mSecondHandColor = array.getColor(R.styleable.AlarmClockView_secondHandColor, getResources().getColor(R.color.green)); mMinuteHandColor = array.getColor(R.styleable.AlarmClockView_minuteHandColor, getResources().getColor(R.color.black)); mHourHandColor = array.getColor(R.styleable.AlarmClockView_hourHandColor, getResources().getColor(R.color.black)); mMinuteScaleColor = array.getColor(R.styleable.AlarmClockView_minuteScaleColor, getResources().getColor(R.color.black)); mPointScaleColor = array.getColor(R.styleable.AlarmClockView_scaleColor, getResources().getColor(R.color.black)); mDateValueColor = array.getColor(R.styleable.AlarmClockView_dateValueColor, getResources().getColor(R.color.black)); mIsShowTime = array.getBoolean(R.styleable.AlarmClockView_isShowTime, true); mProportion = array.getFloat(R.styleable.AlarmClockView_proportion, (float) 0.75); if (mProportion > 1 || mProportion < 0) { mProportion = (float) 0.75; } array.recycle(); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWid = w; mHei = h; //使鬧鐘的寬爲控件寬的mProportion; mClockWid = (int) (w * mProportion); mOuterRadius = mClockWid / 2; mInnerRadius = mOuterRadius - mSpace; mCenterX = w / 2; mCenterY = mCenterX; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //設置整體控件的背景爲白色背景 mPaint.setColor(Color.WHITE); canvas.drawRect(0, 0, mWid, mHei, mPaint); //畫外層圓 drawOuterCircle(canvas); //畫內層圓 drawInnerCircle(canvas); //畫刻度 drawTickMark(canvas); //畫刻度值 drawScaleValue(canvas); //畫針 drawHand(canvas); //畫現在時間顯示 if (mIsShowTime) { drawCurrentTime(canvas); } } /** * 畫時鐘底部的時間文本 * * @param canvas */ private void drawCurrentTime(Canvas canvas) { mPaint.setColor(mDateValueColor); mPaint.setAntiAlias(true); mPaint.setTextSize(40); //使當前時間文本正好在時鐘底部距離有2 * mSpace的位置 Paint.FontMetricsInt fm = mPaint.getFontMetricsInt(); int baseLineY = mCenterY + mOuterRadius - fm.top + 2 * mSpace; String time = "" + mYear + "年" + (mMonth + 1) + "月" + mDay + "日" + mWeekStr + mHour + "點" + mMinute + "分" + mSecond + "秒"; canvas.drawText(time, mCenterX, baseLineY, mPaint); } /** * 畫時鐘內的針 * * @param canvas */ private void drawHand(Canvas canvas) { //畫時針 canvas.save(); int hourWid = 16; mPaint.setColor(mHourHandColor); mPaint.setStrokeWidth(hourWid); for (int i = 1; i <= 12; i++) { canvas.rotate(30, mCenterX, mCenterY); if (i == mHour) { //計算時針的偏移量 int offset = (int) (((float) mMinute / (float) 60) * (float) 30); canvas.rotate(offset, mCenterX, mCenterY); RectF rectF = new RectF(mCenterX - hourWid / 2, mCenterY - mInnerRadius + mScaleValueHei + 3 * mSpace, mCenterX + hourWid / 2, mCenterY); canvas.drawRoundRect(rectF, hourWid / 2, hourWid / 2, mPaint); break; } } canvas.restore(); //畫分針 canvas.save(); int minuteWid = 10; mPaint.setColor(mMinuteHandColor); mPaint.setStrokeWidth(10); for (int i = 0; i < 60; i++) { if (i == mMinute) { //計算分針的偏移量 int offset = (int) ((float) mSecond / (float) 60 * (float) 6); canvas.rotate(offset, mCenterX, mCenterY); RectF rectF = new RectF(mCenterX - minuteWid / 2, mCenterY - mInnerRadius + 3 * mSpace, mCenterX + minuteWid / 2, mCenterY); canvas.drawRoundRect(rectF, minuteWid / 2, minuteWid / 2, mPaint); break; } else { canvas.rotate(6, mCenterX, mCenterY); } } canvas.restore(); //畫秒針 canvas.save(); mPaint.setColor(mSecondHandColor); mPaint.setStrokeWidth(3); canvas.drawCircle(mCenterX, mCenterY, mSpace, mPaint); for (int i = 0; i < 60; i++) { if (i == mSecond) { canvas.drawLine(mCenterX, mCenterY + 3 * mSpace, mCenterX, mCenterY - mInnerRadius + mSpace, mPaint); break; } else { canvas.rotate(6, mCenterX, mCenterY); } } canvas.restore(); } /** * 畫時鐘內的刻度值 * * @param canvas */ private void drawScaleValue(Canvas canvas) { mPaint.setColor(mPointScaleColor); mPaint.setStyle(Paint.Style.FILL); mPaint.setStrokeWidth(5); mPaint.setAntiAlias(true); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setTextSize(30); //計算刻度值的文本高度 Paint.FontMetricsInt fm = mPaint.getFontMetricsInt(); mScaleValueHei = fm.bottom - fm.top; for (int i = 0; i < 12; i++) { String degree = (i + 1) + ""; float[] temp = calculatePoint((i + 1) * 30, mInnerRadius - mSpace * 4 - mPaint.getTextSize() / 2); canvas.drawText(degree, temp[2] + mCenterX, mCenterY + temp[3] + mPaint.getTextSize() / 2, mPaint); } } /** * 計算線段的起始座標 * * @param angle * @param length * @return */ private float[] calculatePoint(float angle, float length) { int POINT_BACK_LENGTH = 1; float[] points = new float[4]; if (angle <= 90f) { points[0] = -(float) Math.sin(angle * Math.PI / 180) * POINT_BACK_LENGTH; points[1] = (float) Math.cos(angle * Math.PI / 180) * POINT_BACK_LENGTH; points[2] = (float) Math.sin(angle * Math.PI / 180) * length; points[3] = -(float) Math.cos(angle * Math.PI / 180) * length; } else if (angle <= 180f) { points[0] = -(float) Math.cos((angle - 90) * Math.PI / 180) * POINT_BACK_LENGTH; points[1] = -(float) Math.sin((angle - 90) * Math.PI / 180) * POINT_BACK_LENGTH; points[2] = (float) Math.cos((angle - 90) * Math.PI / 180) * length; points[3] = (float) Math.sin((angle - 90) * Math.PI / 180) * length; } else if (angle <= 270f) { points[0] = (float) Math.sin((angle - 180) * Math.PI / 180) * POINT_BACK_LENGTH; points[1] = -(float) Math.cos((angle - 180) * Math.PI / 180) * POINT_BACK_LENGTH; points[2] = -(float) Math.sin((angle - 180) * Math.PI / 180) * length; points[3] = (float) Math.cos((angle - 180) * Math.PI / 180) * length; } else if (angle <= 360f) { points[0] = (float) Math.cos((angle - 270) * Math.PI / 180) * POINT_BACK_LENGTH; points[1] = (float) Math.sin((angle - 270) * Math.PI / 180) * POINT_BACK_LENGTH; points[2] = -(float) Math.cos((angle - 270) * Math.PI / 180) * length; points[3] = -(float) Math.sin((angle - 270) * Math.PI / 180) * length; } return points; } /** * 畫時鐘的刻度線 * * @param canvas */ private void drawTickMark(Canvas canvas) { canvas.save(); for (int i = 0; i < 60; i++) { if (i % 5 == 0) { mPaint.setColor(mPointScaleColor); mPaint.setStyle(Paint.Style.FILL); mPaint.setStrokeWidth(5); mPaint.setAntiAlias(true); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setTextSize(30); canvas.drawLine(mCenterX, mSpace * 2 + mCenterY - mOuterRadius, mCenterX, mSpace * 4 + mCenterY - mOuterRadius, mPaint); } else { mPaint.setColor(mMinuteScaleColor); mPaint.setStyle(Paint.Style.FILL); mPaint.setStrokeWidth(2); mPaint.setAntiAlias(true); canvas.drawLine(mCenterX, mSpace * 2 + mCenterY - mOuterRadius, mCenterX, mSpace * 3 + mCenterY - mOuterRadius, mPaint); } canvas.rotate(6, mCenterX, mCenterY); } canvas.restore(); } /** * 畫內圓 * * @param canvas */ private void drawInnerCircle(Canvas canvas) { mPaint.setColor(mInnerCircleColor); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(2); canvas.drawCircle(mCenterX, mCenterY, mInnerRadius, mPaint); mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); canvas.drawCircle(mCenterX, mCenterY, mInnerRadius - mSpace, mPaint); } /** * 畫外圓 * * @param canvas */ private void drawOuterCircle(Canvas canvas) { mPaint.setColor(mOuterCircleColor); mPaint.setStyle(Paint.Style.STROKE); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(2); canvas.drawCircle(mCenterX, mCenterY, mOuterRadius, mPaint); } /** * 獲取當前時間 */ public void initCurrentTime() { Calendar mCalendar = Calendar.getInstance(); //因爲獲取的時間總是晚一秒,這裏加上這一秒 mCalendar.add(Calendar.SECOND, 1); mYear = mCalendar.get(Calendar.YEAR); mMonth = mCalendar.get(Calendar.MONTH); mDay = mCalendar.get(Calendar.DAY_OF_MONTH); mWeek = mCalendar.get(Calendar.DAY_OF_WEEK); mHour = mCalendar.get(Calendar.HOUR); mMinute = mCalendar.get(Calendar.MINUTE); mSecond = mCalendar.get(Calendar.SECOND); Calendar calendar = Calendar.getInstance(); //1.數組下標從0開始;2.老外的第一天是從星期日開始的 mWeekStr = arr[calendar.get(calendar.DAY_OF_WEEK) - 1]; System.out.println("現在時間:小時:" + mHour + ",分鐘:" + mMinute + ",秒:" + mSecond); if (listener != null) { listener.onTimeChange(mCalendar); } invalidate(); } /** * 運行鬧鐘 * * @param listener */ public void start(TimeChangeListener listener) { this.listener = listener; mHandler.postDelayed(runnable, 1000); initCurrentTime(); } /** * 運行鬧鐘 */ public void start() { mHandler.postDelayed(runnable, 1000); initCurrentTime(); } /** * 停止鬧鐘 */ public void stop() { mHandler.removeCallbacks(runnable); } }
import java.util.Calendar; /** * Created by HARRY on 2019/1/4 0004. */ public interface TimeChangeListener { void onTimeChange(Calendar calendar); }
步驟1.將JitPack存儲庫添加到構建文件中
項目的根build.gradle中添加以下代碼:
allprojects { repositories { ... maven { url 'https://jitpack.io' } } }
步驟2.build.gradle添加依賴項
dependencies { implementation 'com.github.hnsycsxhzcsh:AlarmClockView:v1.2' }
步驟3. 佈局中引用控件
<com.alarmclockview.AlarmClockView android:id="@+id/clock" app:proportion="0.6" android:layout_width="match_parent" android:layout_height="match_parent" />
步驟4. activity中調用start啓動鬧鐘並添加監聽
mClock = findViewById(R.id.clock); //運行鬧鐘 mClock.start(new TimeChangeListener() { @Override public void onTimeChange(Calendar calendar) { //根據calendar獲取當前時間 } });
可以在github上下載我的項目:https://github.com/hnsycsxhzcsh/AlarmClockView,如果我的博客對你有幫助的話,歡迎博客點贊支持,並在github右上角star支持!