簡單介紹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 入門(三)簡單自定義 ViewAndroidView
- Android自定義View:快遞時間軸實現AndroidView
- Android 自定義 View 實現橫行時間軸AndroidView
- flutter-簡單實現找妹子自定義viewFlutterView
- Android自定義View之實現簡單炫酷的球體進度球AndroidView
- 自定義View合輯(1)-時鐘View
- Android自定義View:View(二)AndroidView
- 簡單介紹SpringMVC RESTFul實現列表功能SpringMVCREST
- Android自定義view之實現帶checkbox的SnackbarAndroidView
- Android 自定義 View 實戰之 PuzzleViewAndroidView
- Android自定義View整合AndroidView
- 【朝花夕拾】Android自定義View篇之(四)自定義View的三種實現方式及自定義屬性詳解AndroidView
- 【自定義View】抖音網紅文字時鐘-上篇View
- Android自定義view-自繪ViewAndroidView
- Flutter自定義View的實現FlutterView
- Android通過輔助功能實現搶微信紅包原理簡單介紹Android
- 重拾Android自定義ViewAndroidView
- Android自定義View:ViewGroup(三)AndroidView
- Android 自定義 View 之 LeavesLoadingAndroidView
- android自定義view(自定義數字鍵盤)AndroidView
- 自定義View:Paint的常用屬性介紹及使用ViewAI
- 直播軟體開發,Android自定義簡單的音訊波譜viewAndroid音訊View
- 【朝花夕拾】Android自定義View篇之(八)多點觸控(上)MotionEvent簡介AndroidView
- Bootstrap Blazor 元件介紹 Table (二)自定義模板列功能介紹bootBlazor元件
- 直播系統程式碼,Android自定義View實現呼吸燈效果AndroidView
- 直播平臺原始碼,Android自定義View實現呼吸燈效果原始碼AndroidView
- Android技術分享|【自定義View】實現Material Design的Loading效果AndroidViewMaterial Design
- Android自定義View實現流式佈局(熱門標籤效果)AndroidView
- Android自定義View之分貝儀AndroidView
- Android自定義View之捲尺AndroidView
- Android自定義View注意事項AndroidView
- Android自定義View-卷軸AndroidView
- Android自定義View 水波氣泡AndroidView
- Android 自定義View 點贊效果AndroidView
- Android 自定義View基礎(一)AndroidView
- android自定義View——座標系AndroidView
- 簡單介紹numpy實現RNN原理實現RNN
- 簡單介紹NMS的實現方法