blog

カスタム時計表示

効果画像に最初に:カスタムビューは非常に強力である知っている、クールなUI効果の多くを達成することができ、ハンズオンプレイされていない、プロジェクトは、ほぼすべてのニーズをカスタマイズする必要が発生し...

Jun 23, 2020 · 7 min. read
シェア

まずは効果写真:

カスタムビューは非常に強力であることを知って、クールなUI効果の多くを達成することができ、ハンズオンプレイされていない、プロジェクトは、需要をカスタマイズする必要が発生した他の人のための検索はほとんど常に使用するために特に良い完成されています!

過去2日間で、私は他の誰かのブログは、図2に似た時計を持って見た、私はこれを達成する方法について考えたので、私は小さなデモを書きました。

ここでは、いくつかの簡単なカスタムプロパティを追加しました:

res->valuesの下に新しいattrs.xmlを作成します。

<declare-styleable name="Clock">
 <attr name="scaleColor" format="color" />
 <attr name="circleColor" format="color" />
 <attr name="defScaleColor" format="color" />
 <attr name="hourHandColor" format="color" />
 <attr name="minuteHandColor" format="color" />
 <attr name="secondHandColor" format="color" />
 <attr name="centerIcon" format="reference" />
</declare-styleable>

初期化時に割り当てが行われます:

var ta: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.Clock)
 scaleColor = ta.getColor(R.styleable.Clock_scaleColor, Color.parseColor("#666666"))
 circleColor = ta.getColor(R.styleable.Clock_circleColor, Color.parseColor("#666666"))
 defScaleColor = ta.getColor(R.styleable.Clock_defScaleColor, Color.parseColor("#666666"))
 hourHandColor = ta.getColor(R.styleable.Clock_hourHandColor, Color.parseColor("#666666"))
 minuteHandColor =
 ta.getColor(R.styleable.Clock_minuteHandColor, Color.parseColor("#666666"))
 secondHandColor =
 ta.getColor(R.styleable.Clock_secondHandColor, Color.parseColor("#fc0101ff"))

ここでのcenterIconは、図1の中央にある小さなアイコンで、アイコンのResourceIdを取得してここに描画します。以下は、アイコンのサイズを設定するための関連コードで、初期化が完了した後にta.recycle()を実行することを覚えておいてください:

//センターアイコンのサイズを設定する
 var centerIconResourceId = ta.getResourceId(R.styleable.Clock_centerIcon, R.mipmap.star)
 centerIcon = BitmapFactory.decodeResource(context.resources, centerIconResourceId)
 var width = centerIcon.width
 var height = centerIcon.height
 var newWidth = 100
 var newHeight = 100
 val scaleWidth = newWidth.toFloat() / width
 val scaleHeight = newHeight.toFloat() / height
 //行列を拡大縮小する
 val matrix = Matrix()
 matrix.postScale(scaleWidth, scaleHeight)
 centerIcon = Bitmap.createBitmap(centerIcon, 0, 0, width, height, matrix, true)
 ta.recycle()

プロパティの準備ができたら、次はブラシの準備です:

 /*スケールブラシ*/
 private var scalePaint = Paint()
 /*時計ブラシ*/
 private var hourHandPaint = Paint()
 /*分筆*/
 private var minuteHandPaint = Paint()
 /*秒ブラシ*/
 private var secondHandPaint = Paint()
 /*センターアイコン*/
 private var centerIconPaint = Paint()
 /*円周ブラシ*/
 private var circlePaint = Paint()
 /*パスブラシ*/
 private var pathPaint = Paint()

先ほどプロパティを初期化したコードで、ブラシも初期化します:

 circlePaint.color = circleColor
 circlePaint.isAntiAlias = true
 circlePaint.style = Paint.Style.STROKE
 circlePaint.strokeWidth = 4f
 scalePaint.color = scaleColor
 scalePaint.isAntiAlias = true
 scalePaint.style = Paint.Style.STROKE
 hourHandPaint.color = hourHandColor
 hourHandPaint.strokeWidth = 10f
 hourHandPaint.isAntiAlias = true
 hourHandPaint.style = Paint.Style.FILL_AND_STROKE
 minuteHandPaint.color = minuteHandColor
 minuteHandPaint.isAntiAlias = true
 minuteHandPaint.strokeWidth = 7f
 minuteHandPaint.style = Paint.Style.FILL_AND_STROKE
 secondHandPaint.color = secondHandColor
 secondHandPaint.isAntiAlias = true
 secondHandPaint.strokeWidth = 4f
 secondHandPaint.style = Paint.Style.FILL_AND_STROKE
 pathPaint.color = circleColor
 pathPaint.isAntiAlias = true
 pathPaint.strokeWidth = 4f
 pathPaint.style = Paint.Style.FILL

次のステップは、初期化メソッドで現在時刻を取得することです:

 var calendar = Calendar.getInstance()
 hours = calendar.get(Calendar.HOUR)
 minutes = calendar.get(Calendar.MINUTE)
 seconds = calendar.get(Calendar.SECOND)
//時計をリフレッシュしてスタートする
 start()

良い状態になるための準備作業の後、いくつかのコアステップ、onMeasure、onDrawのカスタムビューを開始しました。

最初に onMeasure を渡してサイズを設定し、中心点の位置と時計の半径を決定します:

 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec)
 var width: Int = getMeasureSize(true, widthMeasureSpec)
 var height: Int = getMeasureSize(false, heightMeasureSpec)
 setMeasuredDimension(width, height)
 }
 private fun getMeasureSize(isWidth: Boolean, measureSpec: Int): Int {
 var result = 0
 var specSize = MeasureSpec.getSize(measureSpec)
 var specMode = MeasureSpec.getMode(measureSpec)
 when (specMode) {
 MeasureSpec.AT_MOST -> {
 result = if (isWidth) {
 specSize.coerceAtMost(mWidth)
 } else {
 specSize.coerceAtMost(mheight)
 }
 }
 MeasureSpec.EXACTLY -> {
 result = specSize
 }
 MeasureSpec.UNSPECIFIED -> {
 if (isWidth) {
 result = suggestedMinimumWidth
 } else {
 result = suggestedMinimumHeight
 }
 }
 }
 return result
 }
 override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
 super.onSizeChanged(w, h, oldw, oldh)
 mWidth = w
 mheight = h
 mCenterX = w / 2.0f
 mCenterY = h / 2.0f
 mClockRadius = (h / 2.0f) * 0.9f
 }

コントロールのサイズが決まったら、onDraw() で描画を開始します。

図1の時計は次のように描画されます:

canvas.rotate(r)メソッドを使用したスケールとポインタの描画では、私は、描画が描画するための水平軸座標0の位置のように見ることができるときに、キャンバスが回転します何度であることを理解しています。

各描画後、キャンバスが復元されます

 override fun onDraw(canvas: Canvas?) {
 super.onDraw(canvas)
 //ここでは、キャンバスの中心にキャンバスの開始点を移動することである、中心点の座標は(0)になる,0)
 canvas?.translate(mCenterX, mCenterY)
 drawCircle(canvas)
 drawScale(canvas)
 drawHour(canvas)
 drawminute(canvas)
 drawSeconds(canvas)
 drawCenterIcon(canvas)
 }
 /* */
 private fun drawSeconds(canvas: Canvas?) {
 //分針は1分で6 上がる。
 Log.i("cx----canvasSeconds", "" + seconds)
 canvas?.save()
 canvas?.rotate(seconds * 6f)
 canvas?.drawLine(0f, 10f, 0f, -mClockRadius + 40f, secondHandPaint)
 canvas?.restore()
 }
 /* */
 private fun drawminute(canvas: Canvas?) {
 //分針は1分で6度上がる。
 canvas?.save()
 canvas?.rotate(minutes * 6f)
 canvas?.drawLine(0f, 10f, 0f, -mClockRadius + 80f, minuteHandPaint)
 canvas?.restore()
 }
 /* */
 private fun drawHour(canvas: Canvas?) {
 //時針は1時間で30度、分針は1分で0度ずつ進む。.5度
 canvas?.save()
 canvas?.rotate(hours * 30f + minutes * 0.5f)
 canvas?.drawLine(0f, 10f, 0f, -mClockRadius + 120f, hourHandPaint)
 canvas?.restore()
 }
 /*ダイヤルセンターのアイコンを描く*/
 private fun drawCenterIcon(canvas: Canvas?) {
 canvas?.save()
 //ここでの-50は上記のビットマップ量の半分であり、中心点が始点であるため、実際には描画マークの座標は-50となる。,-50
 canvas?.drawBitmap(centerIcon, -50f, -50f, centerIconPaint)
 canvas?.restore()
 }
 /* */
 private fun drawScale(canvas: Canvas?) {
 for (i in 0 until 60) {
 if (i % 5 == 0) {
 //特別なスケール
 scalePaint.setColor(scaleColor)
 scalePaint.strokeWidth = 6f
 canvas?.drawLine(0f, -mClockRadius + 4f, 0f, -mClockRadius + 30f, scalePaint)
 } else {
 //一般的な規模
 scalePaint.setColor(defScaleColor)
 scalePaint.strokeWidth = 3f
 canvas?.drawLine(0f, -mClockRadius + 4f, 0f, -mClockRadius + 20f, scalePaint)
 }
 //各スケールごとに6度ずつキャンバスを回転させる。
 canvas?.rotate(6f)
 }
 }
 /*時計の外側の円を描く。*/
 private fun drawCircle(canvas: Canvas?) {
 canvas?.drawCircle(0f, 0f, mClockRadius, circlePaint)
 }

図2の時計は以下のように描画されます。

 override fun onDraw(canvas: Canvas?) {
 super.onDraw(canvas)
 //ここでは、キャンバスの中心にキャンバスの開始点を移動することである、中心点の座標は(0)になる,0)
 canvas?.translate(mCenterX, mCenterY)
 drawPath(canvas)
 }
 /*3本の手で三角形を描く。*/
 private fun drawPath(canvas: Canvas?) {
 canvas?.save()
 var path = Path()
 //角度を計算し、ここでマイナス90は12時位置によると、開始位置であり、それは-90である°
 var hoursAngle = hours * 30f + minutes * 0.5f - 90
 var minutesAngle = minutes * 6f - 90
 var secondsAngle = seconds * 6f - 90
 //計算点の時針、分針、秒針の座標の角度に応じて
 var xHours =
 (mClockRadius - 120f) * kotlin.math.cos(hoursAngle * 3.14 / 180)
 var yHours =
 (mClockRadius - 120f) * kotlin.math.sin(hoursAngle * 3.14 / 180)
 var xMinutes =
 (mClockRadius - 80f) * kotlin.math.cos(minutesAngle * 3.14 / 180)
 var yMinutes =
 (mClockRadius - 80f) * kotlin.math.sin(minutesAngle * 3.14 / 180)
 var xSeconds =
 (mClockRadius - 40f) * kotlin.math.cos(secondsAngle * 3.14 / 180)
 var ySeconds =
 (mClockRadius - 40f) * kotlin.math.sin(secondsAngle * 3.14 / 180)
 //座標点を結んで三角形を描く。
 path.moveTo(xHours.toFloat(), yHours.toFloat())
 path.lineTo(xMinutes.toFloat(), yMinutes.toFloat())
 path.lineTo(xSeconds.toFloat(), ySeconds.toFloat())
 path.close()
 canvas?.drawPath(path, pathPaint)
 canvas?.restore()
 }

ポインタのスケールが描画され、今、クロックが動いてみましょう、非常に単純な、タイマーを定義し、初期化でタイミングを開始することができ、postInvalidate()のタイミングは、レイアウトを更新するために使用されます:

/**
 *  
 */
 private val mTimer = Timer()
 private val task: TimerTask = object : TimerTask() {
 override fun run() {
 var calendar = Calendar.getInstance()
 hours = calendar.get(Calendar.HOUR)
 minutes = calendar.get(Calendar.MINUTE)
 seconds = calendar.get(Calendar.SECOND)
 postInvalidate()
 }
 }
 /**
 * タイマーをオンにする
 */
 fun start() {
 mTimer.schedule(task, 0, 1000)
 }

ここでは、カスタムクロックを完了するには、拡張機能を行うには、カスタムプロパティで独自のプロパティを追加することができ、開始の完了の始まりは想像ほど複雑ではありませんが、ここでレコードを行うには、また、印象を深めるための手順を介して櫛の最初から自分自身を聞かせていることです。

Read next

データ構造とアルゴリズム - 動的計画法

動的計画法はアルゴリズム設計の手法の一つ。 問題を重複する部分問題に分解し、その部分問題を繰り返し解くことで、元の問題を繰り返し解くことによって、元の問題を解きます。 注:Divide and conquerは、互いに独立した部分問題に分解することを強調しています。

Jun 23, 2020 · 2 min read