Android自定義View分享——一個水平的進度條 [復制鏈接]

2018-9-18 10:42
jjcodecode 閱讀:529 評論:0 贊:0
Tag:  

寫在前面

筆者近來在學習Android自定義View,收集了一些不算復雜但又“長得”還可以的自定義View效果實現,這些View的邏輯不算復雜,大多都只用到了Paint、Canvas類的一些常用的API。在后續的博客里面,將分享幾個不同的效果,本文作為第一篇,先來一個很簡單的——一個水平進度條(跟系統的那個不同)。

本文適合什么樣的人

如果你接觸自定義View不久,看懂了View繪制基本流程,知道onMeasured()、onLayout()、onDraw(),知道Paint、Canvas類及其API很強大但沒用過,正準備大顯身手來幾個自定義View卻又不知道該“畫”一個怎么樣自定義View比較合適,苦惱于網上各種開源項目太高端,看不懂,那么本文及后續的博客很適合你,來讓我們一起拿起鉛筆(Paint)、白紙(Canvas)來當一回藝術家唄。

效果展現

看這就是我們要的效果,朝兩邊滾動的進度條。 

設計圖

作為一個藝術家(碼農),當然要先有設計圖,才能“畫”出一個靠譜的自定義View,你要認真看我的設計圖,盡管文章最后面會給出完整代碼地址,不過如果你認真看設計圖,完全可以自己畫哦~~

第一個情況設計圖

好了根據設計圖能看到:

  • 我們的View以控件的中間為分界線,分別向左右兩邊滾動。
  • 每個bar長度都是一樣的,除了左右兩邊的第一個。另外相鄰bar之間的間距也是一樣。
  • 我們需要關注三個參數,第一個bar的寬度,其他普通bar的寬度,兩個bar之間的間隔。

我們只要繪制一邊,另一邊是對稱的,所以繪制的邏輯是:

  1. 從中間開始,先繪制第一個較特殊的bar,然后間隔barSpace距離,繪制第二個寬度為barWidth的bar,重復,直到你的繪制位置超出控間的右邊界。
  2. 繪制完成,同樣操作對左邊也繪制一次,注意傳入API里面的關于坐標相關的參數。此時完成一次繪制,注意了只是一次,我們需要將firstBarWidth變大一些,然后調用invalidate()方法重繪,這樣才能顯示出動態的效果。

代碼片段如下所示:

//第一個"bar"寬度小于普通"bar",注意我的坐標系原點在控件中間
if(firstBarWidth<=barWidth){
    //繪制右邊第一個“條”
    //index是繪制索引
    int index = 0;
    canvas.drawLine(index, 0, firstBarWidth, 0, barPaint);
    index+=firstBarWidth+barSpace;
    //循環繪制右邊其它普通的“條”
    while (index <= measuredWidth/2){
        canvas.drawLine(index, 0, index+barWidth, 0, barPaint);
        index+=barWidth+barSpace;
    }
    //繪制左邊第一個“條”
    index = 0;
    canvas.drawLine(-firstBarWidth, 0, index, 0, barPaint);
    index-=(firstBarWidth+barSpace);
    //循環繪制左邊其它普通的“條”
    while (index >= -measuredWidth/2){
        canvas.drawLine(index-barWidth, 0, index, 0, barPaint);
        index-=(barWidth+barSpace);
    }
    firstBarWidth+=barWidth/10;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

第二個情況設計圖

為什么會有兩種情況,你是否想過,當firstBarWidth(第一個bar的寬度)達到普通的bar寬度時,該怎么辦?直接將變量重置為0,從頭畫過?絕對不是,仔細想想你會發現,如果你這樣做的話,會讓滾動條有一個被“抽”一下的感覺,這種思考是錯誤的,那么正確的是怎樣,來看第二個設計圖 

當第一個bar達到了普通bar的寬度,我們就要執行另外一個繪制邏輯:

  1. 從繪制起點開始,先間隔一小段的距離(長度為firstBarDistance),才開始繪制第一個bar。
  2. 然后間隔barWidth距離,繪制第二個,重復,直到繪制起點超過控件邊界。這樣就完成了一次繪制,類似于第一個情況,為了體現動態效果,我們需要將firstBarDistance變大一些,然后調用invalidate()方法重繪,這樣才能顯示出動態的效果。
  3. 問題在于,firstBarDistance達到什么程度才好,其實當他的值等于barSpace時,就是第二個情況的臨界點了,在下一刻,就回到了第一個情況的起點,此時只要重置所有計算參數就可以。

代碼片段如下所示:

//第一個"bar"寬度達到普通的"bar"寬度時
//循環繪制右邊的“條”
float index = firstBarDistance;
while (index <= measuredWidth/2){
    canvas.drawLine(index, 0, index+barWidth, 0, barPaint);
    index+=barWidth+barSpace;
}
//循環繪制左邊的“條”
index = -firstBarDistance;
while (index >= -measuredWidth/2){
    canvas.drawLine(index-barWidth, 0, index, 0, barPaint);
    index-=(barWidth+barSpace);
}
firstBarDistance+=barWidth/10;
//到達臨界點,全部重置
if(firstBarDistance > barSpace) {
    firstBarDistance = barWidth/10;
    firstBarWidth = barWidth/10;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

注意一下

針對我們這個View的特點,在你正式繪圖之前,我提一下:

  • 在onDraw()里面,在所有的繪圖操作執行之前,調用一下這個方法:
canvas.translate(measuredWidth/2, getMeasuredHeight()/2);
  • 1

讓坐標系移到中間,可以簡化一些計算。

  • 提前看看canvas.drawLine()方法,在畫線時不要傳錯參數了哦。
  • invalidate()方法可以引發控間重繪,如果你想控制重繪頻率,postInvalidateDelayed();方法是個不錯的替代品。
  • 沒錯,除了一些數學計算之外,我們就僅僅用到了Paint對象基本創建,Canvas類的drawLine()方法、translate()方法,就是這么簡單啦。

項目源碼: 
http://www.iddrgr.tw/thread-603005-1-1.html


我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

两码中特期期