自定義View(四)之芝麻信用

先來看效果圖,gif圖效果不是很好,可以在真機上看一波:


螞蟻信用.gif

仔細分析效果:
接到UI設(shè)計的效果,你覺得我會怎么做?

一般的步驟:
  • Google一波,沒有找到類似的效果,所以就老老實實的分析一下效果,該如何實現(xiàn)?
  • 內(nèi)外圓弧、刻度、文字、動畫、模糊、漸變的效果,分析完之后就開始吧:
螞蟻信用.png
畫內(nèi)圓:
private fun drawInnerCircle(canvas: Canvas) {
    canvas.save()
    val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    paint.maskFilter = BlurMaskFilter(2f, BlurMaskFilter.Blur.NORMAL)
    paint.alpha = 0x70
    paint.style = Paint.Style.STROKE
    paint.strokeWidth = dp2px(mInnerStrokeWidth.toInt()).toFloat()
    canvas.translate(measuredWidth / 2f, measuredHeight / 2f)
    val radius = Math.min(measuredWidth / 4f, measuredHeight / 4f)
    val rectF = RectF(-radius, -radius, radius, radius)
    canvas.drawArc(rectF, mStartAngle, mSweepAngle, false, paint)
    canvas.restore()
}
分析:
  • BlurMaskFilter:模糊效果我是用的是內(nèi)外都模糊繪制(BlurMaskFilter.Blur.NORMA);
  • canvas.translate(measuredWidth / 2f, measuredHeight / 2f),將canvas的圓點坐標移至View的中心,方便計算;
外圓:
 private fun drawOutCircle(canvas: Canvas) {
    canvas.save()
    val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    paint.setShadowLayer(dp2px(5).toFloat(), 2f, 2f, Color.BLACK)
    paint.alpha = 0x70
    paint.style = Paint.Style.STROKE
    paint.strokeWidth = dp2px(mOutStrokeWidth.toInt()).toFloat()

    canvas.translate(measuredWidth / 2f, measuredHeight / 2f)
    val radius = Math.min(measuredWidth / 4f, measuredHeight / 4f) + mInnerStrokeWidth + paint.strokeWidth + dp2px(5)
    val rectF = RectF(-radius, -radius, radius, radius)
    canvas.drawArc(rectF, mStartAngle, mSweepAngle, false, paint)
    canvas.restore()
}

這里就不分析了,和畫外圓一樣,注意:paint.setShadowLayer(dp2px(5).toFloat(), 2f, 2f, Color.BLACK),需要關(guān)閉硬件加速,因為在Android中,只有文本才支持陰影,其他GG了。

畫刻度:
 private fun drawScale(canvas: Canvas) {
    canvas.save()
    val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    paint.color = Color.RED
    paint.alpha = 0x70
    paint.style = Paint.Style.STROKE
    paint.strokeWidth = dp2px(2).toFloat()
    canvas.translate(measuredWidth / 2f, measuredHeight / 2f)
    val radius = Math.min(measuredWidth / 4f, measuredHeight / 4f)
    canvas.rotate(-270 + mStartAngle)
    for (index in 0..mScacleNumber) {
        canvas.drawLine(0f, -radius + dp2px(mInnerStrokeWidth.toInt()) / 2.toFloat(), 0f, -radius - dp2px(mInnerStrokeWidth.toInt()).toFloat() / 2, paint)
        canvas.rotate(mScaleValue)
    }
    canvas.restore()
}

分析:
mStartAngle開始畫圓弧的角度,上面畫內(nèi)外圓都是直接給的值,不需要旋轉(zhuǎn),而這里為什么要旋轉(zhuǎn)坐標系,其實是為了好計算坐標,這里逆時針旋轉(zhuǎn)-270+mStartAngle,剛好和前面畫的內(nèi)圓開始的mStartAngle相同位置,不信你計算一下。然后循環(huán)mSweepAngle / mScacleNumber刻度,這里就順時針旋轉(zhuǎn)了,其中有些API如果不清楚作用這時候就該點擊官網(wǎng)看看了。

畫進度條、小圓點:
private fun drawIndicator(canvas: Canvas) {
    canvas.save()
    canvas.translate(measuredWidth / 2f, measuredHeight / 2f)
    val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    paint.maskFilter = BlurMaskFilter(2f, BlurMaskFilter.Blur.NORMAL)
    paint.alpha = 0x70
    paint.color = Color.RED
    paint.style = Paint.Style.STROKE
    paint.strokeWidth = dp2px(mOutStrokeWidth.toInt()).toFloat()
    val intArrayOf = intArrayOf(0xffffffff.toInt(), 0x00ffffff, 0x99ffffff.toInt(), 0xffffffff.toInt())
    paint.shader = SweepGradient(measuredWidth / 2f, measuredHeight / 2f, intArrayOf, null)

    val sweep = currentProgress.toFloat() / mMaxProgress * mSweepAngle
    val radius = Math.min(measuredWidth / 4f, measuredHeight / 4f) + mInnerStrokeWidth + paint.strokeWidth + dp2px(5)
    val rectF = RectF(-radius, -radius, radius, radius)
    canvas.drawArc(rectF, mStartAngle, sweep, false, paint)


    /*
        畫小圓點
     */
    paint.style = Paint.Style.FILL
    val y = Math.sin(Math.toRadians((sweep + mStartAngle).toDouble())) * radius
    val x = Math.cos(Math.toRadians((sweep + mStartAngle).toDouble())) * radius
    canvas.drawCircle(x.toFloat(), y.toFloat(), dp2px(3).toFloat(), paint)
    canvas.restore()
}

其實也簡單,注意:三角函數(shù)使用的是弧度,用到一些數(shù)學(xué)知識,自行百度,這里就不做介紹了,模糊效果和漸變效果BlurMaskFilter和SweepGradient,還是自行官網(wǎng)

最后就是畫文本:
  private fun drawText(canvas: Canvas) {
    canvas.save()
    canvas.translate(measuredWidth / 2f, measuredHeight / 2f)
    val paint = TextPaint(Paint.ANTI_ALIAS_FLAG)
    paint.textSize = dp2px(25).toFloat()
    paint.color = Color.WHITE
    val textClass = getClassText()
    val rect2 = Rect()
    paint.getTextBounds(textClass, 0, textClass.length, rect2)
    canvas.drawText(textClass, 0, textClass.length, -rect2.width() / 2f, rect2.height() / 2f + dp2px(5), paint)

    paint.reset()
    paint.textSize = dp2px(25).toFloat()
    paint.color = Color.WHITE
    val radius = Math.min(measuredWidth / 4f, measuredHeight / 4f)
    val str = "$currentProgress"
    val rect = Rect()
    paint.getTextBounds(str, 0, str.length, rect)
    canvas.drawText(str, 0, str.length, -rect.width() / 2f, rect.height() / 2f - radius / 3, paint)

    canvas.restore()
}

更新進度這里我用了屬性動畫,所以還是看碼吧:

 /**
 * 進度更新
 */
fun update(progress: Int) {
    val tmpCurrentProgress = if (progress >= mMaxProgress) mMaxProgress else progress
    mIndicatorAnimator = ObjectAnimator.ofInt(this, "currentProgress", tmpCurrentProgress)
    mIndicatorAnimator?.apply {
        this.duration = 2000
        this.interpolator = BounceInterpolator()
        this.start()
    }
}

注意:這個屬性必須提供getter和setter方法,畢竟屬性動畫嘛,操作的就是屬性:

 fun setCurrentProgress(currentProgress: Int) {
    this.currentProgress = currentProgress
    invalidate()
}

fun getCurrentProgress() = currentProgress

如何使用?

    <com.kotlin.hc.one.AntClassView
        android:id="@+id/mCustomView"
        android:layout_width="400dp"
        android:layout_height="400dp"
        android:layout_centerInParent="true"
        app:max_progress="800"
        app:start_angle="150"
        app:sweep_angle="245"
        app:text_size="25sp" />

其實自定義View并不難,可能ViewGroup會難一些,復(fù)雜一點的其實也不難,主要的難點在于如何結(jié)合去計算位置,還有就是性能了。
demo源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,222評論 3 119
  • 行香子·春吟 ——李印中 柳搖鵝黃,蘭沁幽窗。小院里、鶯囀燕忙。朧步閑庭,試吟春光。得沁園春,醉花引,燕歸梁。 縱...
    明燭高照閱讀 312評論 0 1
  • 積愁垂云雨濕棠,孤影形單立長廊。飛燕南去春不歸。黯然傷,薄衫難抵寒夜涼。 泣血撫琴鳳求凰,幽語驚醒夢一場。霓裳消隱...
    快樂的憨豆閱讀 483評論 2 4
  • 在杭州的每個早晨,小巷子大街角,充盈著蔥香鮮味兒。杭州老百姓最愛的早點,并不是摩登洋氣的西式果醬三明治,也不是通...
    嘎嘣六三三閱讀 882評論 6 17

友情鏈接更多精彩內(nèi)容