成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

這篇文章給大家分享的是有關(guān)如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來(lái)看看吧。

十余年的東興網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。網(wǎng)絡(luò)營(yíng)銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整東興建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)建站從事“東興網(wǎng)站設(shè)計(jì)”,“東興網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

先看看做的是什么

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

看完圖,我們來(lái)整理下思路

  • 餅圖居中,每塊區(qū)域都是一個(gè)扇形,需要canvas.drawArc根據(jù)角度來(lái)繪制

  • 需要path.arcTo定位到扇形弧度的一半來(lái)繪制折線的起點(diǎn)

  • 通過canvas.drawPath繪制折線,折線的長(zhǎng)度根據(jù)餅圖大小來(lái)設(shè)置比例

  • 通過canvas.drawText繪制文字,文字的大小根據(jù)餅圖的大小來(lái)設(shè)置比例,繪制文字的位置需要計(jì)算文字的寬度

思路清晰后就擼起袖子加油干

知識(shí)點(diǎn)

我們先來(lái)了解一個(gè)概念,我們?cè)趐aint畫扇形的時(shí)候,對(duì)應(yīng)的度數(shù)是在哪個(gè)位置呢?

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

看到圖后應(yīng)該明白了吧

繪制餅圖

我們先來(lái)看看他的參數(shù),很明顯,左、上、右、下參數(shù)形成一個(gè)面板,startAngle 為起始的角度,sweepAngle 為從起始角度開始繪制多少度,useCenter為是否連接到圓心,paint為畫筆

 public void drawArc(float left, float top, float right, float bottom, float startAngle,
 float sweepAngle, boolean useCenter, @NonNull Paint paint) {
 super.drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
 }

我們以當(dāng)前控件的width、height為面板來(lái)畫一個(gè)圓形的餅圖

 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 override fun onDraw(canvas: Canvas) {
 super.onDraw(canvas)
 canvas.drawArc(0f, 0f, width, height, 0f, 360f, true, paintRed)
 }

哇塞,好丑哦,結(jié)果顯示的是一個(gè)橢圓,如果要繪制一個(gè)圓形的餅圖,我們必須得保證left=top=right=bottom

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

設(shè)置餅圖居中

 /**
 * view的寬度
 */
 var width: Float = 0f
 /**
 * view的高度
 */
 var height: Float = 0f
 /**
 * drawArc距離左邊的距離
 */
 var left: Float = 0f
 /**
 * drawArc距離上邊的距離
 */
 var top: Float = 0f
 /**
 * drawArc距離右邊的距離
 */
 var right: Float = 0f
 /**
 * drawArc距離下邊的距離
 */
 var bottom: Float = 0f
 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 override fun onDraw(canvas: Canvas) {
 super.onDraw(canvas)
 canvas.drawArc(left, top, right, bottom, 0f, 360f, true, paint)
 } 
 override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
 super.onSizeChanged(w, h, oldw, oldh)
 setBackgroundColor(resources.getColor(R.color.black))
 width = w.toFloat()
 height = h.toFloat()
 left = width / 4f
 top = width / 4f
 right = width - left
 bottom = width - top 
 }

完美居中

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

接下來(lái),我們要把上面從0度到360度多分幾個(gè)步驟來(lái)繪制

 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 override fun onDraw(canvas: Canvas) {
 super.onDraw(canvas)
 ...
 canvas.drawArc(left, top, right, bottom, 0f, 20f, true, paintPuple)
 canvas.drawArc(left, top, right, bottom, 20f, 10f, true, paintGray)
 canvas.drawArc(left, top, right, bottom, 30f, 40f, true, paintGreen)
 canvas.drawArc(left, top, right, bottom, 70f, 110f, true, paintBlue)
 canvas.drawArc(left, top, right, bottom, 180f, 110f, true, paintRed)
 canvas.drawArc(left, top, right, bottom, 290f, 70f, true, paintYellow)
 }

還不錯(cuò)

上圖的度數(shù)是寫死的,現(xiàn)在我們來(lái)把他寫活

提供一個(gè)設(shè)置個(gè)數(shù)的集合,比如農(nóng)名伯伯賣水果,梨子賣了10個(gè),香蕉賣了3個(gè),蘋果賣了7個(gè),那么這個(gè)個(gè)數(shù)的集合為pieList=(10,3,7)。

因?yàn)轱瀳D是根據(jù)角度來(lái)繪制的,我們必須將這個(gè)個(gè)數(shù)集合換算成角度集合,換算的過程中我們需要知道每一種水果所占總水果的比例,然后通過這個(gè)比例去乘上360度,就知道每一種水果所占的度數(shù)。

梨子的占比為10/(10+3+7)=1/2,可得梨子占餅圖的度數(shù)為1/2*360=180度,按照這種方式計(jì)算,香蕉和蘋果占餅圖的度數(shù)分別為54度和126度,那么,餅圖的分布也就出來(lái)了

現(xiàn)在,我們來(lái)定義一個(gè)個(gè)數(shù)集合,計(jì)算出比例的集合和度數(shù)的集合,下面是比例的集合,度數(shù)的集合我們?cè)诶L制的時(shí)候再去計(jì)算

 /**
 * 個(gè)人分類集合
 */
 var pieList = arrayListOf(10f,3f,7f)

 /**
 * 餅圖所占的比例
 */
 var scaleList = arrayListOf<Float>()
 /**
 * 個(gè)數(shù)分類的總量
 */
 var total: Float = 0f 
 override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
 super.onSizeChanged(w, h, oldw, oldh)
 //計(jì)算個(gè)數(shù)的總和
 total = pieList.sum()
 //存儲(chǔ)比例值
 for (a in pieList) {
  scaleList.add(a.div(total))
 }
 }

比例集合拿到了,接下來(lái),我們?nèi)パh(huán)這個(gè)比例值,然后將比例值乘上360度,計(jì)算出角度值,供drawArc的sweepAngle使用,但是,我們還缺少一個(gè)startAngle起始角度,我們可以定義一個(gè)起始角度為0度,然后每次根據(jù)計(jì)算出的角度值sweepAngle去累加起始度數(shù),用代碼來(lái)實(shí)現(xiàn)下

 /**
 * 記錄當(dāng)前畫餅圖的度數(shù)
 */
 var currentDegree: Float = 0f

 /**
 * 累加餅圖的度數(shù)作為下一個(gè)繪制的起始度數(shù)
 */
 var srctorDegree: Float = 0f 
 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 override fun onDraw(canvas: Canvas) {
 super.onDraw(canvas) 
 for (scale in scaleList) {
  val paint = Paint()
  paint.strokeWidth = dip(10.0f).toFloat()
  paint.isAntiAlias = true
  //定義一個(gè)隨機(jī)生成的顏色數(shù),來(lái)區(qū)分不同的扇形區(qū)域
  val hex = "#" + Integer.toHexString((-16777216 * Math.random()).toInt())
  paint.color = Color.parseColor(hex)
  //角度數(shù)
  srctorDegree = scale * 360
  canvas.drawArc(left, top, right, bottom, currentDegree, srctorDegree, true, paint)
  //累加角度
  currentDegree += srctorDegree
 }
 }

ok,現(xiàn)在我們可以隨機(jī)的去定義個(gè)數(shù)來(lái)生成占比的餅圖了

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

繪制折線

接下來(lái),我們來(lái)繪制折線,折線的起點(diǎn)是每個(gè)扇形弧上的一半,path的arcTo方法也可以繪制圓,且方法參數(shù)使用也是一樣,我們可以讓arcTo跟著canvas.drawArc一塊畫,arcTo的startAngle起始角度為canvas.drawArc起始角度加上sweepAngle度數(shù)的一半,這樣,就定位到了弧邊的一半,arcTo的sweepAngle為0就行了,我們只定位,不繪制

  ...
  canvas.drawArc(left, top, right, bottom, currentDegree, srctorDegree, true, paint)
  val path = Path()
  path.arcTo(left, top, right, bottom, currentDegree + srctorDegree / 2, 0f, false)
  ...

現(xiàn)在,path的位置定位到弧邊的一半了,接下來(lái),我們要知道當(dāng)前path的坐標(biāo)然后根據(jù)坐標(biāo)去繪制折線,

  val bounds = RectF()
  //將path當(dāng)前的坐標(biāo)賦值給bounds
  path.computeBounds(bounds, true)

現(xiàn)在拿到坐標(biāo)了,我們?cè)賮?lái)看看效果圖,折線和文字呈四個(gè)方向,我們不如把餅圖分成四個(gè)區(qū)域,以圓心為坐標(biāo)軸原點(diǎn),切分四個(gè)象限:

  • 第一象限:折線為右上,文字在折線右邊

  • 第二象限:折線為左上,文字在折線左邊

  • 第三象限:折線為左下,文字在折線左邊

  • 第四象限:折線為右下,文字在折線右邊

那么,接下來(lái)就是如何判斷當(dāng)前起始點(diǎn)在哪個(gè)象限了,先以第一象限為例,如果當(dāng)前的坐標(biāo)大于餅圖橫軸方向一半,并且小于餅圖縱軸方向的一半,那么就是第一象限,其他依次類推

 /**
 * 橫線的長(zhǎng)度
 */
 var lineae: Int = 30

 /**
 * 斜線的長(zhǎng)度
 */
 var slantLine: Int = 30 
 override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
 super.onSizeChanged(w, h, oldw, oldh)
  //計(jì)算橫線的比例
  lineae = (width / 30f).toInt()
  //計(jì)算斜線的比例
  slantLine = (width / 40f).toInt()
 }
 
 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 override fun onDraw(canvas: Canvas) {
 super.onDraw(canvas)

 for (scale in scaleList) {
  ...
  val path = Path()
  path.arcTo(left, top, right, bottom, currentDegree + srctorDegree / 2, 0f, false)
  val bounds = RectF()
  path.computeBounds(bounds, true) 
  //第一象限
  if (bounds.left >= width / 2 && bounds.top <= width / 2) {
  path.lineTo(bounds.left + lineae, bounds.top)
  path.lineTo(bounds.left + lineae + slantLine, bounds.top - slantLine)
  canvas.drawPath(path, paintLine)
  //第二象限
  } else if (bounds.left <= width / 2 && bounds.top <= width / 2) {
  path.lineTo(bounds.left - lineae, bounds.top)
  path.lineTo(bounds.left - lineae - slantLine, bounds.top - slantLine)
  canvas.drawPath(path, paintLine)
  //第三象限
  } else if (bounds.left <= width / 2 && bounds.top >= width / 2) {
  path.lineTo(bounds.left - lineae, bounds.top)
  path.lineTo(bounds.left - lineae - slantLine, bounds.top + slantLine)
  canvas.drawPath(path, paintLine)
  //第四象限
  } else {
  path.lineTo(bounds.left + lineae, bounds.top)
  path.lineTo(bounds.left + lineae + slantLine, bounds.top + slantLine)
  canvas.drawPath(path, paintLine)
  } 
  }
  ...
 }

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖   

哎呀,出來(lái)了

繪制文字

接下來(lái)就是繪制文字了,第一、四象限還好,文字可以在折線后面跟著畫,但是二、三象限的文字就不允許了,我們必須往前移動(dòng)文字寬度的距離才能完美銜接到折線上,所以,我們來(lái)定義一個(gè)計(jì)算文字的方法

 /**
 * 獲取文字的寬度
 */
 private fun getStringWidth(str: String): Float = paintLine.measureText(str)

文字是會(huì)隨著餅圖的大小進(jìn)行改變的,所以設(shè)置文字大小的比例

 paintLine.textSize = dip(width / 100).toFloat()

接下來(lái)就開始繪制文字吧

 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 override fun onDraw(canvas: Canvas) {
 super.onDraw(canvas)
  ... 
  //獲取當(dāng)前的百分比文字
  val textStr = String.format("%.2f%%", scale * 100)
  //獲取文字的寬度
  val textWidth = getStringWidth(textStr)   
  //第一象限
  if (bounds.left >= width / 2 && bounds.top <= width / 2) {
  ...
  canvas.drawText(textStr, bounds.left + lineae + slantLine, bounds.top - slantLine, paintText)
  ...
  //第二象限
  } else if (bounds.left <= width / 2 && bounds.top <= width / 2) {
  ...
  canvas.drawText(textStr, bounds.left - lineae - slantLine - textWidth, bounds.top - slantLine, paintText)
  ...
  //第三象限
  } else if (bounds.left <= width / 2 && bounds.top >= width / 2) {
  ...
  canvas.drawText(textStr, bounds.left - lineae - slantLine - textWidth, bounds.top + lineae, paintText)
  ...
  //第四象限
  } else {
  ...
  canvas.drawText(textStr, bounds.left + lineae + slantLine, bounds.top + slantLine, paintText)
  ...
  }  
 }

嗯,還不錯(cuò),

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

然后我們?cè)倏纯葱Ч麍D,餅圖中間還有一塊與背景色一樣的黑圓,這不跟簡(jiǎn)單了嘛

 //定義中間黑圓的畫筆
 paintCicle.color = resources.getColor(R.color.black)
 paintCicle.isAntiAlias = true
 paintCicle.style = Paint.Style.FILL 
  @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
  override fun onDraw(canvas: Canvas) {
  super.onDraw(canvas)  
  ...  
  //在循環(huán)結(jié)束餅圖的時(shí)候,以餅圖的原點(diǎn)為中心畫圓 
  canvas.drawCircle(width / 2, width / 2, width / 8, paintCicle)
  }

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

然后我們暴露一個(gè)方法,提供給Activity去調(diào)用

 /**
  * 設(shè)置扇形參數(shù)
  */
 fun setPieData(a: ArrayList<Float>) {
  pieList.clear()
  pieList.addAll(a)
  invalidate()
 }

那么,Activity就可以這么去調(diào)用了

 override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_second)
  pie1.setPieData(arrayListOf(1f,10f,15f,9f,15f))
  pie2.setPieData(arrayListOf(3f,8f,15f,7f,9f))
  pie3.setPieData(arrayListOf(9f,3f,7f,3f,4f,2f,1f))
 }

如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖

感謝各位的閱讀!關(guān)于“如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

本文名稱:如何使用kotlin實(shí)現(xiàn)一個(gè)餅圖
標(biāo)題網(wǎng)址:http://jinyejixie.com/article4/jjigie.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作用戶體驗(yàn)、網(wǎng)站內(nèi)鏈、、小程序開發(fā)、商城網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營(yíng)
湖北省| 溧水县| 康马县| 朔州市| 和静县| 阿拉善左旗| 九江市| 德昌县| 读书| 新余市| 渝中区| 道真| 喀喇| 布拖县| 渭源县| 韩城市| 碌曲县| 綦江县| 金门县| 曲周县| 湘乡市| 聊城市| 东乌珠穆沁旗| 武隆县| 屯门区| 隆子县| 虎林市| 乐东| 洛宁县| 贵定县| 平湖市| 大足县| 新竹市| 定南县| 防城港市| 禹州市| 萨嘎县| 兰州市| 阿尔山市| 泸西县| 壤塘县|