自定義view詳解,手把手帶你畫一個漂亮蜂窩view Android自定義view

這個效果做起來好像沒什麼意義,如果不加監聽回調 圖片就能直接替代。寫這篇博客的目的是鍛鍊一下思維能力,以更好的面多各種自定義view需求。

本文是和代碼同步寫的。也就是說在寫文章的時候才敲的代碼。這樣會顯得文章有些許混亂。但是我想這樣記錄下來,一個自定義view的真正的製作過程,是一點一點,一步步跟着思路的改變,完善的。不可能一下子就做出一個完整的view。。技術也是這樣,不可能一步登天。都是一步一步的積累。

另外,每一篇博客都是建立在之前博客的基礎知識上的,如果你剛接觸自定義view。可以來說說自定義view簡單學習的方式這裏看我以前的文章。記錄了我學習自定義view的過程,而且前幾篇博客或多或少犯了一些錯誤。這裏我並不想改正博文中的錯誤,因爲些錯誤是大家經常會犯的,後來的博客都有指出這些錯誤,以及不再犯,這是一個學習的過程。所以我想把錯誤的經歷記錄下來。等成爲高手 回頭看看當年的自己是多麼菜。。也會有成就感。。

老規矩效果圖如下:

\

首先畫一個六邊形,畫之前來計算一下六邊形的相關知識:

\

假設一個正六邊形的邊長爲a ,因爲每個角都是120° 所以可得高爲根號三a ,如圖所示。

有了這些信息我們就可以繪製一個六邊形出來,如下:

?
1
2
3
4
5
6
7
8
9
float height = (float) (Math.sqrt(3)*mLength);
       mPath.moveTo(mLength/2,0);
       mPath.lineTo(0,height/2);
       mPath.lineTo(mLength/2,height);
       mPath.lineTo((float) (mLength*1.5),height);
       mPath.lineTo(2*mLength,height/2);
       mPath.lineTo((float) (mLength*1.5),0);
       mPath.lineTo(mLength/2,0);
       mPath.close();

繪製效果:

\

 

然後將其根據一個偏移量進行平移,就可以用循環繪製出多個六邊形

 

這裏offset是偏移量,緊挨着的話應該是偏移一個六邊形的寬,寬由上圖可知爲 a/2+a+a/2 即 2a;

 

?
1
2
3
4
5
6
7
8
9
10
11
for(int i = 0 ; i < 3;i++) {
           int offset = mLength * 2 * i;
           mPath.moveTo(mLength / 2 + offset, 0);
           mPath.lineTo(0 + offset, height / 2);
           mPath.lineTo(mLength / 2 + offset, height);
           mPath.lineTo((float) (mLength * 1.5) + offset, height);
           mPath.lineTo(2 * mLength + offset, height / 2);
           mPath.lineTo((float) (mLength * 1.5)+offset, 0);
           mPath.lineTo(mLength / 2+offset, 0);
           mPath.close();
       }

發現效果如下

 

\

這不對啊,很奇怪啊。。 底下空出來的一個三角形放不下我們的第二行啊。。

那麼怎麼辦呢。。 加大offset! 加大多少呢。。 應該多增加一個邊長。。這樣就正好留空了。 來試試

\

現在來準備畫第二行....

發現我們之前path的座標都是相對寫死的。。 所以要回過頭改一下,改成給定一個起點,就可以繪製出一個六邊形,經過計算,得出下圖

\

這裏a代表邊長。

改完之後的代碼是:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
float height = (float) (Math.sqrt(3)*mLength);
       for(int i = 0 ; i < 3;i++) {
 
           //橫座標偏移量
           int offset = mLength * 3 * i ;
           //左上角的x
           int x = mLength/2 + offset;
           int y = 0;
 
           //根據左上角一點 繪製整個正六邊形
           mPath.moveTo(x, y);
           mPath.lineTo(x -mLength/2, height / 2 + y);
           mPath.lineTo(x, height+y);
           mPath.lineTo(x + mLength, height +y);
           mPath.lineTo((float) (x + 1.5*mLength), height / 2+y);
           mPath.lineTo(x + mLength, y);
           mPath.lineTo(x, y);
           mPath.close();
       }

繪製出來的效果是一樣的。但是方法以及變了。

然後來畫第二行,第二行起點的path應該在這裏

\

座標是: 2a , height/2 這裏的偏移量不變。

首先將畫path的方法提取出來(as快捷鍵ctrl + alt + m)

?
1
2
3
4
5
6
7
8
9
10
11
//根據左上角一點 繪製整個正六邊形
   private void getPath(float height, float x, float y) {
       mPath.moveTo(x, y);
       mPath.lineTo(x -mLength/2, height / 2 + y);
       mPath.lineTo(x, height+y);
       mPath.lineTo(x + mLength, height +y);
       mPath.lineTo((float) (x + 1.5*mLength), height / 2+y);
       mPath.lineTo(x + mLength, y);
       mPath.lineTo(x, y);
       mPath.close();
   }
然後再給個循環,來繪製第二行的六邊形
?
1
2
3
4
5
6
7
8
9
10
11
for(int i = 0;i<2;i++){
 
    float offset = mLength * 3 * i ;
    float x = mLength*2 + offset;
    float y = height/2;
 
    getPath(height,x,y);
 
}
 
canvas.drawPath(mPath,mPaint);
得到如下的效果。

\

現在ondraw的全部代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Override
  protected void onDraw(Canvas canvas) {
      mPaint.setColor(Color.parseColor("#FFBB33"));
      //正六邊形的高
      float height = (float) (Math.sqrt(3)*mLength);
      for(int i = 0 ; i < 3;i++) {
 
          //橫座標偏移量
          float offset = mLength * 3 * i ;
          //左上角的x
          float x = mLength/2 + offset;
          float y = 0;
 
          getPath(height, x, y);
      }
 
      canvas.drawPath(mPath,mPaint);
      mPath.reset();
 
      mPaint.setColor(Color.parseColor("#AA66CC"));
 
      for(int i = 0;i<2;i++){
 
          float offset = mLength * 3 * i ;
          float x = mLength*2 + offset;
          float y = height/2;
 
          getPath(height,x,y);
 
 
      }
 
      canvas.drawPath(mPath,mPaint);
  }

接下來對每行的個數進行一下控制。
?
1
2
3
4
5
//每行的個數
  private int mColumnsCount = 3;
 
  //行數
  private int mLineCount = 3;

對應的循環也改變,最外面套一個大循環,來控制多行繪製
?
1
2
3
4
for (int j = 0; j < mLineCount; j++) {
         if(j%2 == 0)  繪製奇數行 else 繪製偶數行
 
}
現在整個ondraw如下。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Override
    protected void onDraw(Canvas canvas) {
        //正六邊形的高
        float height = (float) (Math.sqrt(3) * mLength);
 
        for (int j = 0; j < mLineCount; j++) {
            if (j % 2 == 0) {
                mPaint.setColor(Color.parseColor("#FFBB33"));
 
                for (int i = 0; i < mColumnsCount; i++) {
 
                    //橫座標偏移量
                    float offset = mLength * 3 * i;
                    //左上角的x
                    float x = mLength / 2 + offset;
                    float y = j * height / 2;
 
                    getPath(height, x, y);
 
 
                }
                canvas.drawPath(mPath, mPaint);
                mPath.reset();
            } else {
 
                mPaint.setColor(Color.parseColor("#AA66CC"));
 
                for (int i = 0; i < mColumnsCount; i++) {
 
                    float offset = mLength * 3 * i;
                    float x = mLength * 2 + offset;
                    float y = (height / 2) * j;
 
                    getPath(height, x, y);
 
 
                }
 
                canvas.drawPath(mPath, mPaint);
                mPath.reset();
            }
 
 
        }
 
 
    }
\

好像顏色一樣就不好看了。。那我們來動態改變一下顏色..

添加一個屬性list來存放color

?
1
private ArrayList<integer> mColorList;</integer>
?
1
2
3
4
5
6
7
8
9
10
11
mColorList = new ArrayList<>();
 
       mColorList.add(Color.parseColor("#33B5E5"));
 
       mColorList.add(Color.parseColor("#AA66CC"));
 
       mColorList.add(Color.parseColor("#99CC00"));
 
       mColorList.add(Color.parseColor("#FFBB33"));
 
       mColorList.add(Color.parseColor("#FF4444"));
在循環中,取出顏色值
?
1
2
3
for (int j = 0; j < mLineCount; j++) {
 
           mPaint.setColor(mColorList.get(j));
效果如下:

\

嗯。。看起來像一點樣子了。。。 給中間加點文字吧。。

先給每個蜂窩編號

\

按上面的循環 j爲行數 i爲列數

研究規律發現 編號等於 j*3 + i

我們有六邊形左上角的座標xy 可以輕易的計算出中心座標

\

這些都有了。開一個list存放中間的文字:

?
1
2
//存放文字的list
private ArrayList<string> mTextList ;</string>
在初始化的時候給添加點數據
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  mTextList = new ArrayList<>();
        for(int i =0;i<mlinecount*mcolumnscount;i++){ mtextpaint="new" pre="" wing="">
 
繪製文字: 這裏要注意他和path的繪製順序,如果path後繪製則會覆蓋掉文字<pre class="brush:java;"> float txtLength = mTextPaint.measureText(mTextList.get(txtId));
                    canvas.drawText(mTextList.get(txtId),x+mLength/2-txtLength/2,y+height/2+5, mTextPaint);</pre>
<p>下面是全部的ondraw</p>
<pre class="brush:java;"@Override
    protected void onDraw(Canvas canvas) {
        //正六邊形的高
        float height = (float) (Math.sqrt(3) * mLength);
        for (int j = 0; j < mLineCount; j++) {
 
            mPaint.setColor(mColorList.get(j));
 
            if (j % 2 == 0) {
 
//                mPaint.setColor(Color.parseColor("#FFBB33"));
 
                for (int i = 0; i < mColumnsCount; i++) {
                    int txtId = j*3 +i;
                    //橫座標偏移量
                    float offset = mLength * 3 * i;
                    //左上角的x
                    float x = mLength / 2 + offset;
                    float y = j * height / 2;
 
 
                    mPath.reset();
                    getPath(height, x, y);
 
                    canvas.drawPath(mPath, mPaint);
                    float txtLength = mTextPaint.measureText(mTextList.get(txtId));
                    canvas.drawText(mTextList.get(txtId),x+mLength/2-txtLength/2,y+height/2+5, mTextPaint);
 
                }
            } else {
 
//                mPaint.setColor(Color.parseColor("#AA66CC"));
 
                for (int i = 0; i < mColumnsCount; i++) {
 
                    int txtId = j*3 +i;
                    float offset = mLength * 3 * i;
                    float x = mLength * 2 + offset;
                    float y = (height / 2) * j;
                    mPath.reset();
                    getPath(height, x, y);
                    canvas.drawPath(mPath, mPaint);
                    float txtLength = mTextPaint.measureText(mTextList.get(txtId));
                    canvas.drawText(mTextList.get(txtId),x+mLength/2-txtLength/2,y+height/2+5, mTextPaint);
 
                }
 
 
 
            }
 
 
        }
 
 
    }</pre>
<br>
現在的效果圖如下:
<p><img alt="\" src="http://www.2cto.com/uploadfile/Collfiles/20160125/20160125091503178.jpg" style="width: 377px; height: 453px;"></p>
<p>好,那現在讓他靈活一點。添加各種set方法,比如行數啊 列數啊 邊長啊 文字內容啊 顏色啊之類的。</p>
<pre class="brush:java;"/**
     * 設置列數
     * @param mColumnsCount
     */
    public void setColumnsCount(int mColumnsCount) {
        this.mColumnsCount = mColumnsCount;
        invalidate();
    }
 
    /**
     * 設置行數
     * @param mLineCount
     */
    public void setLineCount(int mLineCount) {
        this.mLineCount = mLineCount;
 
        invalidate();
    }
 
    /**
     * 設置文本數據
     */
    public void setTextList(ArrayList<string> textList) {
        mTextList.clear();
        mTextList.addAll(textList);
 
        invalidate();
    }
 
    /**
     * 設置顏色數據
     * @param colorList
     */
    public void setColorList(ArrayList<integer> colorList) {
        mColorList.clear();
        mColorList.addAll(colorList);
 
        invalidate();
    }</integer></string></pre>
然後 你有沒有忘記測量呢? 只要把最外面的矩形大小給他就行
<pre class="brush:java;">    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 
        if(widthMode == MeasureSpec.AT_MOST){
            widthSize = (int) ((3f*mColumnsCount+0.5f) *mLength);
        }else{
//            throw new IllegalStateException("only support wrap_content");
        }
 
        if(heightMode == MeasureSpec.AT_MOST){
            heightSize = (int) ((mLineCount/2f +0.5f) * (Math.sqrt(3) * mLength));
        }else{
 
//            throw new IllegalStateException("only support wrap_content");
        }
 
 
        setMeasuredDimension(widthSize,heightSize);
 
 
    }</pre>
<p>這下使用wrap_content 來看看view的大小:</p>
<p><img alt="\" src="http://www.2cto.com/uploadfile/Collfiles/20160125/20160125091504190.jpg" style="width: 426px; height: 473px;"></p>
<p>嗯。。測量也對着。。。 這裏我只實現了wrap_content 大家可以以及擴展 讓他支持EXACTLY</p>

轉自:http://www.2cto.com/kf/201601/487139.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章