簡單介紹Android自定義View實現時鐘功能
最近在練習自定義view, 想起之前面試的時候筆試有道題是寫出自定義一個時鐘的關鍵程式碼. 今天就來實現一下. 步驟依然是先分析, 再上程式碼.
實現效果
View分析
時鐘主要分為五個部分:
1、中心點: 圓心位置
2、圓盤: 以中心點為圓心,drawCircle畫個圓
3、刻度:
paint有個aip, setPathEffect可以根據path畫特效, 那麼刻度就可以根據圓的path畫一個矩形path的特效, 並且這個api只會畫特效, 不會畫出圓.
/** * shape: 特效的path, 這裡傳一個矩形 * advance: 兩個特效path之間的間距, 即兩個矩形的left間距 * phase: 特效起始位置的偏移 * style: 原始path拐彎的時候特效path的轉換方式,這裡用ROTATE跟著旋轉即可 */ PathDashPathEffect(Path shape, float advance, float phase, Style style)
刻度又分兩種, 粗一點刻度: 3、6、9、12, 和細一點的刻度. 兩種特效又可以用SumPathEffect合起來畫
SumPathEffect(PathEffect first, PathEffect second)
4、時分秒指標
時分秒指標都是一個圓角矩形, 先把他們的位置計算出來, 然後旋轉圓心去繪製不同角度的指標.
5、動畫效果
TimerTask每隔一秒計算時間, 根據時間去換算當前時分秒指標的角度, 動態變數只有三個角度.
實現原始碼
// // Created by skylar on 2022/4/19. // class ClockView : View { private var mTimer: Timer? = null private val mCirclePaint: Paint = Paint() private val mPointerPaint: Paint = Paint() private val mTextPaint: Paint = Paint() private val mCirclePath: Path = Path() private val mHourPath: Path = Path() private val mMinutePath: Path = Path() private val mSecondPath: Path = Path() private lateinit var mPathMeasure: PathMeasure private lateinit var mSumPathEffect: SumPathEffect private var mViewWidth = 0 private var mViewHeight = 0 private var mCircleWidth = 6f private var mRadius = 0f private var mRectRadius = 20f private var mHoursDegree = 0f private var mMinutesDegree = 0f private var mSecondsDegree = 0f private var mCurrentTimeInSecond = 0L constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) constructor( context: Context, attrs: AttributeSet?, defStyleAttr: Int ) : super(context, attrs, defStyleAttr) init { mCirclePaint.color = Color.BLACK mCirclePaint.isAntiAlias = true mCirclePaint.style = Paint.Style.STROKE mCirclePaint.strokeWidth = mCircleWidth mPointerPaint.color = Color.BLACK mPointerPaint.isAntiAlias = true mPointerPaint.style = Paint.Style.FILL mTextPaint.color = Color.BLACK mTextPaint.isAntiAlias = true mTextPaint.style = Paint.Style.FILL mTextPaint.textSize = 40f } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) mViewWidth = measuredWidth - paddingLeft - paddingRight mViewHeight = measuredHeight - paddingTop - paddingBottom mRadius = mViewWidth / 2 - mCircleWidth mCirclePath.addCircle(0f, 0f, mRadius, Path.Direction.CW) mPathMeasure = PathMeasure(mCirclePath, false) val minutesShapePath = Path() val quarterShapePath = Path() minutesShapePath.addRect(0f, 0f, mRadius * 0.01f, mRadius * 0.06f, Path.Direction.CW) quarterShapePath.addRect(0f, 0f, mRadius * 0.02f, mRadius * 0.06f, Path.Direction.CW) val minutesDashPathEffect = PathDashPathEffect( minutesShapePath, mPathMeasure.length / 60, 0f, PathDashPathEffect.Style.ROTATE ) val quarterDashPathEffect = PathDashPathEffect( quarterShapePath, mPathMeasure.length / 12, 0f, PathDashPathEffect.Style.ROTATE ) mSumPathEffect = SumPathEffect(minutesDashPathEffect, quarterDashPathEffect) val hourPointerHeight = mRadius * 0.5f val hourPointerWidth = mRadius * 0.07f val hourRect = RectF( -hourPointerWidth / 2, -hourPointerHeight * 0.7f, hourPointerWidth / 2, hourPointerHeight * 0.3f ) mHourPath.addRoundRect(hourRect, mRectRadius, mRectRadius, Path.Direction.CW) val minutePointerHeight = mRadius * 0.7f val minutePointerWidth = mRadius * 0.05f val minuteRect = RectF( -minutePointerWidth / 2, -minutePointerHeight * 0.8f, minutePointerWidth / 2, minutePointerHeight * 0.2f ) mMinutePath.addRoundRect(minuteRect, mRectRadius, mRectRadius, Path.Direction.CW) val secondPointerHeight = mRadius * 0.9f val secondPointerWidth = mRadius * 0.03f val secondRect = RectF( -secondPointerWidth / 2, -secondPointerHeight * 0.8f, secondPointerWidth / 2, secondPointerHeight * 0.2f ) mSecondPath.addRoundRect(secondRect, mRectRadius, mRectRadius, Path.Direction.CW) startAnimator() } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) if (canvas == null) { return } canvas.translate((mViewWidth / 2).toFloat(), (mViewHeight / 2).toFloat()) //畫圓盤 mCirclePaint.pathEffect = null canvas.drawPath(mCirclePath, mCirclePaint) //畫刻度 mCirclePaint.pathEffect = mSumPathEffect canvas.drawPath(mCirclePath, mCirclePaint) //時分秒指標 mPointerPaint.color = Color.BLACK canvas.save() canvas.rotate(mHoursDegree) canvas.drawPath(mHourPath, mPointerPaint) canvas.restore() canvas.save() canvas.rotate(mMinutesDegree) canvas.drawPath(mMinutePath, mPointerPaint) canvas.restore() canvas.save() canvas.rotate(mSecondsDegree) canvas.drawPath(mSecondPath, mPointerPaint) canvas.restore() //畫中心點 mPointerPaint.color = Color.WHITE canvas.drawCircle(0f, 0f, mRadius * 0.02f, mPointerPaint) } private fun startAnimator() { val cal = Calendar.getInstance() val hour = cal.get(Calendar.HOUR) //小時 val minute = cal.get(Calendar.MINUTE) //分 val second = cal.get(Calendar.SECOND) //秒 mCurrentTimeInSecond = (hour * 60 * 60 + minute * 60 + second).toLong() if (mTimer == null) { mTimer = Timer() } else { mTimer?.cancel() mTimerTask.cancel() } mTimer?.schedule(mTimerTask, 0, 1000) } private var mTimerTask: TimerTask = object : TimerTask() { override fun run() { mCurrentTimeInSecond++ computeDegree() invalidate() } } //12小時 00:00:00 ~ 12:00:00 private fun computeDegree() { val secondsInOneRoll = 12 * 60 * 60 val currentSeconds = mCurrentTimeInSecond % secondsInOneRoll var leftSeconds = currentSeconds val hours = currentSeconds / 60 / 60 leftSeconds = currentSeconds - hours * 60 * 60 val minutes = leftSeconds / 60 leftSeconds -= minutes * 60 val seconds = leftSeconds % 60 mHoursDegree = hours * 30f mMinutesDegree = minutes * 6f mSecondsDegree = seconds * 6f } }
以上就是本文的全部內容,希望對大家的學習有所幫助。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69901823/viewspace-2923136/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android自定義view實現數字時鐘AndroidView
- 自定義擴充套件jQuery功能簡單介紹套件jQuery
- flutter-簡單實現找妹子自定義viewFlutterView
- jQuery自定義事件簡單介紹jQuery事件
- Android 入門(三)簡單自定義 ViewAndroidView
- Android 自定義 View 實現橫行時間軸AndroidView
- Android自定義View:快遞時間軸實現AndroidView
- 自定義View合輯(1)-時鐘View
- 自定義jquery外掛簡單介紹jQuery
- jQuery自定義外掛簡單介紹jQuery
- Android 自定義 View 之 實現一個多功能的 IndicatorViewAndroidViewIndicator
- 【自定義View】抖音網紅文字時鐘-上篇View
- 自定義View:側滑選單實現View
- Android自定義View之實現簡單炫酷的球體進度球AndroidView
- 實現微信搖一搖功能簡單介紹
- Android 自定義View實戰系列 :時間軸AndroidView
- 自定義View:畫布實現自定義View(折線圖的實現)View
- 自定義View:側滑選單動畫實現View動畫
- jquery自定義事件的使用方式簡單介紹jQuery事件
- android canvas\paint\path簡單使用(自定義view必學)AndroidCanvasAIView
- 簡單介紹SpringMVC RESTFul實現列表功能SpringMVCREST
- Android自定義View實現文字輪播效果AndroidView
- Android通過輔助功能實現搶微信紅包原理簡單介紹Android
- 自定義View:自定義屬性(自定義按鈕實現)View
- 自定義簡單彈幕實現
- Android自定義View:View(二)AndroidView
- Flutter自定義View的實現FlutterView
- 自定義view實現半圓環View
- Android 自定義viewAndroidView
- Android: 自定義ViewAndroidView
- Android自定義View實現微信打飛機遊戲AndroidView遊戲
- AngularJS實現的表單編輯提交功能簡單介紹AngularJS
- 【朝花夕拾】Android自定義View篇之(四)自定義View的三種實現方式及自定義屬性詳解AndroidView
- Android 自定義 View 實戰之 PuzzleViewAndroidView
- Android 自定義 View 實戰之 StickerViewAndroidView
- js實現的陣列自定義排序介紹JS陣列排序
- 自定義View:Paint的常用屬性介紹及使用ViewAI
- Bootstrap Blazor 元件介紹 Table (二)自定義模板列功能介紹bootBlazor元件