Android實(shí)現(xiàn)簡單時(shí)鐘View的方法
通過Canvas的平移與旋轉(zhuǎn)簡化繪圖邏輯是一個(gè)非常有用的技巧,下面的時(shí)鐘view就是利用這個(gè)方法完成的,省去了使用三角函數(shù)計(jì)算坐標(biāo)的麻煩。
package com.example.swt369.simpleclock; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import android.view.ViewTreeObserver; import android.widget.Toast; import java.util.Calendar; /** * Created by swt369 on 2017/8/20. */ public class ClockView extends View { //時(shí)鐘各個(gè)部件的長度 private float mScaleLengthLong; private float mScaleLengthShort; private float mTickLengthHour; private float mTickLengthMinute; private float mTickLengthSecond; //view的寬和高 private float mWidth; private float mHeight; //時(shí)鐘半徑 private float mRadius; //繪制各部件時(shí)用的Paint private Paint mPaintScaleLong; private Paint mPaintScaleShort; private Paint mPaintOutline; private Paint mPaintNum; private Paint mPaintTickHour; private Paint mPaintTickMinute; private Paint mPaintTickSecond; private ViewTreeObserver.OnPreDrawListener onPreDrawListener; public ClockView(final Context context, @Nullable AttributeSet attrs) { super(context, attrs); onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { //獲取view寬高并計(jì)算各個(gè)部件的長度 @Override public boolean onPreDraw() { mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); mRadius = Math.min(mWidth,mHeight) / 2 * 0.95f; mScaleLengthLong = mRadius * 0.1f; mScaleLengthShort = mRadius * 0.05f; mTickLengthHour = mRadius * 0.3f; mTickLengthMinute = mRadius * 0.45f; mTickLengthSecond = mRadius * 0.6f; return true; } }; getViewTreeObserver().addOnPreDrawListener(onPreDrawListener); //點(diǎn)擊顯示具體時(shí)間 setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Calendar calendar = Calendar.getInstance(); String time = String.format("當(dāng)前時(shí)間:%02d:%02d:%02d", calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE),calendar.get(Calendar.SECOND)); Toast.makeText(context,time, Toast.LENGTH_SHORT).show(); } }); //初始化所有Paint對(duì)象 initializePaints(); } @Override protected void onDraw(Canvas canvas) { if(mWidth == 0 || mHeight == 0){ return; } if(onPreDrawListener != null){ getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener); onPreDrawListener = null; } //繪制時(shí)鐘 drawClock(canvas); //一秒后重繪 postInvalidateDelayed(1000); } private void drawClock(Canvas canvas) { //保存原始狀態(tài) canvas.save(); //將坐標(biāo)系原點(diǎn)移到中心,并逆時(shí)針旋轉(zhuǎn)90度。完成后x軸朝上。 canvas.translate(mWidth / 2,mHeight / 2); canvas.rotate(-90); //畫外圍輪廓 canvas.drawCircle(0,0,mRadius, mPaintOutline); //畫刻度 for(int i = 0 ; i < 12 ; i++){ String num = String.valueOf(i == 0 ? 12 : i); if(i % 3 == 0){ canvas.drawLine(mRadius,0,mRadius - mScaleLengthLong,0, mPaintScaleLong); }else { canvas.drawLine(mRadius,0,mRadius - mScaleLengthShort,0, mPaintScaleShort); } canvas.drawText(num,mRadius - mScaleLengthLong - mPaintNum.measureText(num) * 2,0, mPaintNum); //順時(shí)針旋轉(zhuǎn)30度 canvas.rotate(30); } Calendar calendar = Calendar.getInstance(); //畫時(shí)針 int hour = calendar.get(Calendar.HOUR); canvas.save(); canvas.rotate(hour * 30); canvas.drawLine(0,0,mTickLengthHour,0,mPaintTickHour); canvas.restore(); //畫分針 int minute = calendar.get(Calendar.MINUTE); canvas.save(); canvas.rotate(minute * 6); canvas.drawLine(0,0,mTickLengthMinute,0,mPaintTickMinute); canvas.restore(); //畫秒針 int second = calendar.get(Calendar.SECOND); canvas.save(); canvas.rotate(second * 6); canvas.drawLine(0,0,mTickLengthSecond,0,mPaintTickSecond); canvas.restore(); //恢復(fù)原始狀態(tài) canvas.restore(); } private void initializePaints(){ mPaintScaleLong = new Paint(); mPaintScaleLong.setAntiAlias(true); mPaintScaleLong.setStrokeWidth(5); mPaintScaleShort = new Paint(); mPaintScaleShort.setAntiAlias(true); mPaintScaleShort.setStrokeWidth(3); mPaintOutline = new Paint(); mPaintOutline.setStyle(Paint.Style.STROKE); mPaintOutline.setAntiAlias(true); mPaintOutline.setStrokeWidth(5); mPaintNum = new Paint(); mPaintNum.setTextSize(30); mPaintTickHour = new Paint(); mPaintTickHour.setAntiAlias(true); mPaintTickHour.setStrokeWidth(6); mPaintTickMinute = new Paint(); mPaintTickMinute.setAntiAlias(true); mPaintTickMinute.setStrokeWidth(4); mPaintTickSecond = new Paint(); mPaintTickSecond.setAntiAlias(true); mPaintTickSecond.setStrokeWidth(2); } }
代碼注釋已經(jīng)比較詳細(xì)了,下面把比較重要的部分再說明一下:
//保存原始狀態(tài) canvas.save(); //省略中間部分... //恢復(fù)原始狀態(tài) canvas.restore();
在繪圖開始時(shí)調(diào)用canvas.save(),可以保存下未經(jīng)任何平移、旋轉(zhuǎn)操作的原始畫布狀態(tài)。在所有繪圖工作完成后調(diào)用canvas.restore(),可以恢復(fù)到上一次保存的狀態(tài)(類似進(jìn)出棧的感覺)。
//將坐標(biāo)系原點(diǎn)移到中心,并逆時(shí)針旋轉(zhuǎn)90度。完成后x軸朝上。 canvas.translate(mWidth / 2,mHeight / 2); canvas.rotate(-90);
畫布的平移與旋轉(zhuǎn)可能比較抽象,可以想象成坐標(biāo)系的平移與旋轉(zhuǎn)。調(diào)用canvas.translate(dx,dy)相當(dāng)于將坐標(biāo)原點(diǎn)向x,y方向移動(dòng)了dx,dy的距離,調(diào)用canvas.rotate(degree)相當(dāng)于坐標(biāo)系順時(shí)針旋轉(zhuǎn)了degree°。
//畫刻度 for(int i = 0 ; i < 12 ; i++){ String num = String.valueOf(i == 0 ? 12 : i); if(i % 3 == 0){ canvas.drawLine(mRadius,0,mRadius - mScaleLengthLong,0, mPaintScaleLong); }else { canvas.drawLine(mRadius,0,mRadius - mScaleLengthShort,0, mPaintScaleShort); } canvas.drawText(num,mRadius - mScaleLengthLong - mPaintNum.measureText(num) * 2,0, mPaintNum); //順時(shí)針旋轉(zhuǎn)30度 canvas.rotate(30); }
根據(jù)i值決定是畫大刻度(90°的倍數(shù))還是小刻度。每次畫完一條之后將坐標(biāo)系順時(shí)針旋轉(zhuǎn)30°,這樣保證每次畫的刻度線的坐標(biāo)不變。
//一秒后重繪 postInvalidateDelayed(1000);
作用是在1000ms(1s)后再次執(zhí)行View的draw流程,產(chǎn)生指針運(yùn)動(dòng)的效果。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android獲取設(shè)備CPU核數(shù)、時(shí)鐘頻率以及內(nèi)存大小的方法
- Android多功能時(shí)鐘開發(fā)案例(實(shí)戰(zhàn)篇)
- android實(shí)現(xiàn)widget時(shí)鐘示例分享
- Android 仿日歷翻頁、仿htc時(shí)鐘翻頁、數(shù)字翻頁切換效果
- Android多功能時(shí)鐘開發(fā)案例(基礎(chǔ)篇)
- android高仿小米時(shí)鐘(使用Camera和Matrix實(shí)現(xiàn)3D效果)
- Android自定義動(dòng)態(tài)壁紙開發(fā)(時(shí)鐘)
- Android編程基于自定義控件實(shí)現(xiàn)時(shí)鐘功能的方法
- Android仿小米時(shí)鐘效果
- Android自定義View實(shí)現(xiàn)時(shí)鐘功能
相關(guān)文章
android橫豎屏切換時(shí)候Activity的生命周期
曾經(jīng)遇到過一個(gè)面試題,讓你寫出橫屏切換豎屏Activity的生命周期?,F(xiàn)在給大家分析一下他切換時(shí)具體的生命周期是怎么樣的2013-01-01Android中DrawerLayout+ViewPager滑動(dòng)沖突的解決方法
這篇文章主要為大家詳細(xì)介紹了Android中DrawerLayout+ViewPager滑動(dòng)沖突的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06Android開發(fā)調(diào)用WebService的方法示例
這篇文章主要介紹了Android開發(fā)調(diào)用WebService的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android調(diào)用WebService的原理、實(shí)現(xiàn)方法與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2017-10-10Android畫圖實(shí)現(xiàn)MPAndroidchart折線圖示例詳解
這篇文章主要為大家介紹了Android畫圖實(shí)現(xiàn)MPAndroidchart折線圖示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07Android RecycleView添加head配置封裝的實(shí)例
這篇文章主要介紹了Android RecycleView添加head配置封裝的實(shí)例的相關(guān)資料,這里提供實(shí)例幫助大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08Android Binder進(jìn)程間通信工具AIDL使用示例深入分析
Binder作為Android 眾多的IPC通訊手段之一,在Framework的數(shù)據(jù)傳輸中起到極為關(guān)鍵的作用。Binder機(jī)制可謂是Android 知識(shí)體系里的重中之重,作為偏底層的基礎(chǔ)組件,平時(shí)我們很少關(guān)注它,而它卻是無處不在,也是Android 面試易考察的點(diǎn)之一2022-11-11Android編程ViewPager回彈效果實(shí)例分析
這篇文章主要介紹了Android編程ViewPager回彈效果,以實(shí)例形式較為詳細(xì)的分析了ViewPager回彈效果的相關(guān)使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Android項(xiàng)目類似淘寶 電商 搜索功能,監(jiān)聽軟鍵盤搜索事件,延遲自動(dòng)搜索,以及時(shí)間排序的搜索歷史記錄的實(shí)現(xiàn)
本篇文章主要介紹了Android實(shí)現(xiàn)類似淘寶、電商、搜索功能(監(jiān)聽軟鍵盤搜索事件,延遲自動(dòng)搜索,以及時(shí)間排序的搜索歷史記錄),感興趣的小伙伴們可以參考一下。2016-10-10