自定義一個(gè)270°半圓效果

案例分析

最近看到同事UI發(fā)了一個(gè)張圖片拇砰,想到好久都沒有手動畫過圖了,我們先看看我們要的效果圖

image

我們看其中一個(gè)進(jìn)行分析一下狰腌,首先是外圍除破,一個(gè)270°的半圓,灰色的為底部琼腔,黃色的為progress瑰枫。內(nèi)部我們暫時(shí)看作4個(gè)文字,為了方便丹莲,我們暫時(shí)把下劃線看作是百分?jǐn)?shù)的自帶下劃線光坝,看起來他這個(gè)下滑線的長度不會變,我就不多畫一次了甥材。

先畫我們的外圍半圓

    //背景
    backPaint = Paint()
    backPaint.color = Color.GRAY
    backPaint.strokeWidth = 10
    backPaint.isAntiAlias = true
    backPaint.style = Paint.Style.STROKE
    backPaint.strokeCap = Paint.Cap.ROUND
    //進(jìn)度條
    paint = Paint()
    paint.color = Color.RED
    paint.strokeWidth = 10
    paint.isAntiAlias = true
    paint.style = Paint.Style.STROKE
    //拿到如圖有一個(gè)弧形的開始與結(jié)束的線條
    paint.strokeCap = Paint.Cap.ROUND

接著我們測量一下這個(gè)弧形長度盯另。按照圖給的,兩個(gè)半圓洲赵,肯定高度是大于我們控件顯示范圍的鸳惯,目測每個(gè)半圓直徑是在四分之一寬(當(dāng)然UI會給出高度的,我們這里不計(jì)較)叠萍。這樣半徑就是

radius = widths / 8f

然后是我們的角度芝发,可以看到是下方的90°被橫切,而我們的3點(diǎn)鐘方向是對應(yīng)的0°苛谷,通過計(jì)算可知辅鲸,我們是從135°處順時(shí)針旋轉(zhuǎn)270°

//3點(diǎn)鐘方向?yàn)?f
private val startAngle = 135f
private val backSweepAngle = 270f
private var curProgress = 170f
private var curProgress2 = 66f

rectFOne = RectF(widths / 8f, 50f, widths * 3 / 8f, widths / 4f + 50)
rectFTwo = RectF(widths * 5 / 8f, 50f, widths * 7 / 8f, widths / 4f + 50)
private fun drawProgress(canvas: Canvas) {
    canvas.drawArc(rectFOne, startAngle, curProgress, false, paint)
    canvas.drawArc(rectFTwo, startAngle, curProgress2, false, paint)
}

private fun drawBack(canvas: Canvas) {
    canvas.drawArc(rectFOne, startAngle, backSweepAngle, false, backPaint)
    canvas.drawArc(rectFTwo, startAngle, backSweepAngle, false, backPaint)
}
image

看看效果圖(不要在意顏色)

繪出文字

image

按照示意圖這個(gè)排布,我們可以先算一下整個(gè)半圓的高度抄腔。明顯高度d是一個(gè)r+sin45° * r

radius = widths / 8f
d = (Math.sin(Math.PI / 4) * radius + radius).toFloat()

根據(jù)我們畫弧度的reactOne與reactTwo可以拿到兩個(gè)半圓的圓心

centreX = rectFOne.centerX()
centreY = rectFOne.centerY()

centerX = rectFTwo.centerX()
centerY = rectFTwo.centerY()

然后開始我們畫文字瓢湃,百分?jǐn)?shù)我們就把他放在中點(diǎn)理张,上面放在3/7 a的高度,下面依次放在最下端绵患,到3/7 a的高度雾叭。

    //整個(gè)圓弧的高度
private var d = 0f

private var defaultBottomText = "你兩都變了"
private var defaultBottomNext = "我火了你改變了"
private var defaultTopText = "我改變了我火了"
private var defaultCenterText = "63.3%"
private var defaultCenterText2 = "24.5%"

textPaint = Paint()
textPaint.isAntiAlias = true
textPaint.style = Paint.Style.FILL
textPaint.textAlign = Paint.Align.CENTER
textPaint.textSize = d / 12

bigTextPaint = Paint()
bigTextPaint.isAntiAlias = true
bigTextPaint.style = Paint.Style.FILL
bigTextPaint.textAlign = Paint.Align.CENTER
bigTextPaint.textSize = d / 4
bigTextPaint.isUnderlineText = true
bigTextPaint.color = Color.GREEN

private fun drawText(canvas: Canvas) {
    val h = (Math.sin(Math.PI / 4) * radius).toFloat()
    canvas.drawText(defaultBottomText, centreX, centreY + h, textPaint)
    canvas.drawText(defaultBottomNext, centreX, centreY + h * 4 / 7, textPaint)
    canvas.drawText(defaultCenterText, centreX, centreY, bigTextPaint)
    canvas.drawText(defaultTopText, centreX, centreY - radius * 4 / 7, textPaint)

    canvas.drawText(defaultBottomText, centerX, centreY + h, textPaint)
    canvas.drawText(defaultBottomNext, centerX, centreY + h * 4 / 7, textPaint)
    canvas.drawText(defaultCenterText2, centerX, centreY, bigTextPaint)
    canvas.drawText(defaultTopText, centerX, centreY - radius * 4 / 7, textPaint)
}

我們來看看效果

image

目的基本上達(dá)到了,對比下原圖落蝙,到時(shí)候更換下背景织狐,再更換下顏色就會好看點(diǎn)。本來做到這里就完了筏勒,但是呢移迫,我們是有追求的人。彻苄校看網(wǎng)上加載這種數(shù)據(jù)的厨埋,進(jìn)度條應(yīng)該有一個(gè)加載動畫從0到多少多少。現(xiàn)在我們也要來一個(gè)捐顷。

動畫效果

image

唉~加載之后滿滿逼格就上來了荡陷。下面我貼出所有代碼

class DoubleCircleView : View {
private lateinit var backPaint: Paint
private lateinit var paint: Paint
private lateinit var textPaint: Paint
private lateinit var bigTextPaint: Paint

private lateinit var animation: ValueAnimator

private lateinit var rectFOne: RectF
private lateinit var rectFTwo: RectF
private var widths = 0
private var mContext: Context
//第一個(gè)圓弧
private var centreX = 0f
private var centreY = 0f
private var radius = 0f

//第二個(gè)圓弧
private var centerX = 0f
private var centerY = 0f
private var radius2 = 0f

//3點(diǎn)鐘方向?yàn)?f
private val startAngle = 135f
private val backSweepAngle = 270f
//整個(gè)圓弧的高度
private var d = 0f

private var defaultBottomText = "計(jì)算機(jī)設(shè)計(jì)Bottom"
private var defaultBottomNext = "計(jì)算機(jī)設(shè)計(jì)BottomNext"
private var defaultTopText = "計(jì)算機(jī)設(shè)計(jì)Top"
private var defaultCenterText = "0.0%"
private var defaultCenterText2 = "0.0%"


private var curProgress = 0f
private var curProgress2 = 0f

constructor(context: Context) : super(context) {
    mContext = context
    initView()
}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
    mContext = context
    initView()
}

constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
    mContext = context
    initView()
}

private fun initView() {
    widths = getScreenWidth()
    radius = widths / 8f
    d = (Math.sin(Math.PI / 4) * radius + radius).toFloat()


    backPaint = Paint()
    backPaint.color = Color.GRAY
    backPaint.strokeWidth = d / 10
    backPaint.isAntiAlias = true
    backPaint.style = Paint.Style.STROKE
    backPaint.strokeCap = Paint.Cap.ROUND

    paint = Paint()
    paint.color = Color.RED
    paint.strokeWidth = d / 10
    paint.isAntiAlias = true
    paint.style = Paint.Style.STROKE
    paint.strokeCap = Paint.Cap.ROUND


    rectFOne = RectF(widths / 8f, 50f, widths * 3 / 8f, widths / 4f + 50)
    rectFTwo = RectF(widths * 5 / 8f, 50f, widths * 7 / 8f, widths / 4f + 50)

    centreX = rectFOne.centerX()
    centreY = rectFOne.centerY()

    centerX = rectFTwo.centerX()
    centerY = rectFTwo.centerY()

    Log.i("xx", radius.toString())

    textPaint = Paint()
    textPaint.isAntiAlias = true
    textPaint.style = Paint.Style.FILL
    textPaint.textAlign = Paint.Align.CENTER
    textPaint.textSize = d / 12

    bigTextPaint = Paint()
    bigTextPaint.isAntiAlias = true
    bigTextPaint.style = Paint.Style.FILL
    bigTextPaint.textAlign = Paint.Align.CENTER
    bigTextPaint.textSize = d / 4
    bigTextPaint.isUnderlineText = true
    bigTextPaint.color = Color.GREEN


}

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    canvas?.let {
        drawBack(it)
        drawProgress(it)
        drawText(it)
    }
}

private fun drawText(canvas: Canvas) {
    val h = (Math.sin(Math.PI / 4) * radius).toFloat()
    canvas.drawText(defaultBottomText, centreX, centreY + h, textPaint)
    canvas.drawText(defaultBottomNext, centreX, centreY + h * 4 / 7, textPaint)
    canvas.drawText(defaultCenterText, centreX, centreY, bigTextPaint)
    canvas.drawText(defaultTopText, centreX, centreY - radius * 4 / 7, textPaint)

    canvas.drawText(defaultBottomText, centerX, centreY + h, textPaint)
    canvas.drawText(defaultBottomNext, centerX, centreY + h * 4 / 7, textPaint)
    canvas.drawText(defaultCenterText2, centerX, centreY, bigTextPaint)
    canvas.drawText(defaultTopText, centerX, centreY - radius * 4 / 7, textPaint)
    Log.i("xx", d.toString() + ",," + centreY + "radius=" + radius)
}

private fun drawProgress(canvas: Canvas) {
    canvas.drawArc(rectFOne, startAngle, curProgress, false, paint)
    canvas.drawArc(rectFTwo, startAngle, curProgress2, false, paint)
}

private fun drawBack(canvas: Canvas) {
    canvas.drawArc(rectFOne, startAngle, backSweepAngle, false, backPaint)
    canvas.drawArc(rectFTwo, startAngle, backSweepAngle, false, backPaint)
}

private fun getScreenWidth(): Int {
    val service = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val metrics = DisplayMetrics()
    service.defaultDisplay.getMetrics(metrics)
    return metrics.widthPixels
}

fun changeText(pointValue: Float, pointValue2: Float) {
    defaultTopText = "我改變了我火了"
    defaultBottomNext = "我火了你改變了"
    defaultBottomText = "你兩都變了"
    setArcData(0, pointValue)
    setArcData(1, pointValue2)

}
private fun setArcData(position: Int, percent: Float) {
    animation = ValueAnimator.ofFloat(0f, percent)
    animation.duration = 2000
    animation.addUpdateListener {
        val value = it.animatedValue as Float
        val fl = value / 100 * 270
        val s = String.format("%.1f", value) + "%";
        if (position == 0) {
            curProgress = fl
            defaultCenterText = s
        } else if (position == 1) {
            curProgress2 = fl
            defaultCenterText2 = s
        }
        invalidate()
    }
    animation.start()
}
}

使用方法還是貼一下

 <xx.love.view.DoubleCircleView
    android:id="@+id/rv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"/> 


val rv = findViewById<DoubleCircleView>(R.id.rv)
rv.setOnClickListener {
        rv.changeText(63.3f,24.5f)
    }   

希望2019年越來越好_

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市迅涮,隨后出現(xiàn)的幾起案子废赞,更是在濱河造成了極大的恐慌,老刑警劉巖叮姑,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唉地,死亡現(xiàn)場離奇詭異,居然都是意外死亡传透,警方通過查閱死者的電腦和手機(jī)耘沼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旷祸,“玉大人耕拷,你說我怎么就攤上這事讼昆⊥邢恚” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵浸赫,是天一觀的道長闰围。 經(jīng)常有香客問我,道長既峡,這世上最難降的妖魔是什么羡榴? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮运敢,結(jié)果婚禮上校仑,老公的妹妹穿的比我還像新娘忠售。我一直安慰自己,他們只是感情好迄沫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布稻扬。 她就那樣靜靜地躺著,像睡著了一般羊瘩。 火紅的嫁衣襯著肌膚如雪泰佳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天尘吗,我揣著相機(jī)與錄音逝她,去河邊找鬼。 笑死睬捶,一個(gè)胖子當(dāng)著我的面吹牛黔宛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播擒贸,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宁昭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酗宋?” 一聲冷哼從身側(cè)響起积仗,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜕猫,沒想到半個(gè)月后寂曹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡回右,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年隆圆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翔烁。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渺氧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蹬屹,到底是詐尸還是另有隱情侣背,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布慨默,位于F島的核電站贩耐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏厦取。R本人自食惡果不足惜潮太,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铡买,春花似錦更鲁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蛇券,卻和暖如春缀壤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纠亚。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工塘慕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒂胞。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓图呢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骗随。 傳聞我的和親對象是個(gè)殘疾皇子蛤织,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件鸿染、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,103評論 4 62
  • 云遮殘?jiān)滦恼颜?把酒相邀清風(fēng)繞 絲竹欲引九天闕 不若人生肆意好 沒人告訴你 你還活著 或者早已死去 你可能不知道 ...
    古陶子安閱讀 360評論 0 3
  • 小作業(yè) 關(guān)閉害怕失去的通道 ?我害怕失去的財(cái)富有哪些指蚜? 1、害怕失去團(tuán)隊(duì)的業(yè)績獎(jiǎng)金 2涨椒、害怕失去愛人.親人.朋友同...
    西方月閱讀 132評論 0 0
  • 【7】獨(dú)家記憶 “今天太堵車摊鸡,我就知道不能走機(jī)場高速,那條路蚕冬,一年堵365天免猾。”魏然給林欣看著一片紅線的地圖囤热。 “...
    麥麥_ok閱讀 394評論 0 2
  • https://www.cnblogs.com/gossip/p/5969925.html 啟動 [root@iZ...
    回聲2016閱讀 375評論 0 0