http://blog.csdn.net/to_be_designer/article/details/48554469
在之前一篇文章中我们讲解了三种ProgressBar的做法,详见—>《Android 自定义View——自定义ProgressBar 》。这一节中我们模仿360加速球制作一个动态ProgressBar。
当然制作之前,我们先来看看360加速球是什么样子的:
通过上面的动图,我们了解到360加速球是什么样子的,现在我们开始来制作自己的ProgressBar。这里用到了之前两篇博客的知识。大家可以参考学习:
《Android 自定义View——Path的使用 》
《Android 自定义View——自定义ProgressBar 》
《Android PorterDuff.Mode图形混合处理 》
不废话了,接下来进入正题……
原理解析
首先我们定义有一个Bitmap,给这个Bitmap对象定义一个Canvas画布,我们将内容绘制在这个Bitmap上,然后再将Bitmap添加到View的Canvas上。
Bitmap的Canvas上,我们要绘制一个圆形,这个圆形代表最大进度,然后绘制圆形内的“波动的水”。这个波动的水我们要通过处理图形混合的PorterDuff.Mode来实现。“波动的水”的实现,是通过Path中定义贝塞尔曲线完成的。我们绘制一条贝塞尔曲线,通过moveTo()和lineTo()方法,将贝塞尔曲线闭合,然后通过Handler操纵贝塞尔曲线波动。通过PorterDuff.Mode的PorterDuff.Mode.SRC_IN模式上层只显示圆圆形重合的部分,从而实现在贝塞尔曲线在圆形内波动。
代码实现
我们看代码,再通过代码解析:
<code class="language-java hljs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyProgressAnimation</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">View</span> {</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> width;<span class="hljs-comment">//设置高</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> height;<span class="hljs-comment">//设置高</span> <span class="hljs-keyword">private</span> Bitmap bitmap;<span class="hljs-comment">//定义Bitmap</span> <span class="hljs-keyword">private</span> Canvas bitmapCanvas;<span class="hljs-comment">//定义Bitmap的画布</span> <span class="hljs-keyword">private</span> Path mPath; <span class="hljs-comment">//定义路径</span> <span class="hljs-keyword">private</span> Paint mPathPaint;<span class="hljs-comment">//定义路径的画笔</span> <span class="hljs-keyword">private</span> Paint mPaintCircle;<span class="hljs-comment">//定义圆形的画笔</span> <span class="hljs-keyword">private</span> Paint mPaintText; <span class="hljs-comment">//定义绘制文字的画笔</span> <span class="hljs-comment">//设置进度</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> maxProgress = <span class="hljs-number">100</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> currentProgress = <span class="hljs-number">0</span>; <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getMaxProgress</span>() { <span class="hljs-keyword">return</span> maxProgress; } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setMaxProgress</span>(<span class="hljs-keyword">int</span> maxProgress) { <span class="hljs-keyword">this</span>.maxProgress = maxProgress; } <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCurrentProgress</span>() { <span class="hljs-keyword">return</span> currentProgress; } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCurrentProgress</span>(<span class="hljs-keyword">int</span> currentProgress) { <span class="hljs-keyword">this</span>.currentProgress = currentProgress; invalidate();<span class="hljs-comment">//实时更新进度</span> } <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> count = <span class="hljs-number">0</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> NEED_INVALIDATE = <span class="hljs-number">0X6666</span>; <span class="hljs-comment">//操作UI主线程</span> <span class="hljs-keyword">private</span> Handler handler = <span class="hljs-keyword">new</span> Handler() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg) { <span class="hljs-keyword">super</span>.handleMessage(msg); <span class="hljs-keyword">switch</span> (msg.what) { <span class="hljs-keyword">case</span> NEED_INVALIDATE: <span class="hljs-comment">//更新时间</span> count += <span class="hljs-number">5</span>; <span class="hljs-keyword">if</span> (count > <span class="hljs-number">80</span>) { count = <span class="hljs-number">0</span>; } invalidate(); sendEmptyMessageDelayed(NEED_INVALIDATE, <span class="hljs-number">50</span>); <span class="hljs-keyword">break</span>; } } }; <span class="hljs-keyword">public</span> <span class="hljs-title">MyProgressAnimation</span>(Context context, AttributeSet attrs) { <span class="hljs-keyword">super</span>(context, attrs); <span class="hljs-comment">//初始化一个路径</span> mPath = <span class="hljs-keyword">new</span> Path(); <span class="hljs-comment">//初始化绘制路径的画笔</span> mPathPaint = <span class="hljs-keyword">new</span> Paint(); mPathPaint.setAntiAlias(<span class="hljs-keyword">true</span>); mPathPaint.setColor(Color.argb(<span class="hljs-number">0xff</span>, <span class="hljs-number">0xff</span>, <span class="hljs-number">0x69</span>, <span class="hljs-number">0x5a</span>)); mPathPaint.setStyle(Paint.Style.FILL);<span class="hljs-comment">//设置为填充,默认为填充,这里我们还是定义下</span> mPathPaint.setXfermode(<span class="hljs-keyword">new</span> PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); mPaintCircle = <span class="hljs-keyword">new</span> Paint(); mPaintCircle.setAntiAlias(<span class="hljs-keyword">true</span>); mPaintCircle.setColor(Color.argb(<span class="hljs-number">0xff</span>, <span class="hljs-number">0xf8</span>, <span class="hljs-number">0x8e</span>, <span class="hljs-number">0x8b</span>)); mPaintText = <span class="hljs-keyword">new</span> Paint(); mPaintText.setAntiAlias(<span class="hljs-keyword">true</span>); mPaintText.setColor(Color.argb(<span class="hljs-number">0xff</span>, <span class="hljs-number">0xFF</span>, <span class="hljs-number">0xF3</span>, <span class="hljs-number">0xF7</span>)); mPaintText.setTextAlign(Paint.Align.CENTER); mPaintText.setTextSize(<span class="hljs-number">50</span>); handler.sendEmptyMessageDelayed(NEED_INVALIDATE, <span class="hljs-number">50</span>); } <span class="hljs-keyword">public</span> <span class="hljs-title">MyProgressAnimation</span>(Context context) { <span class="hljs-keyword">super</span>(context); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onMeasure</span>(<span class="hljs-keyword">int</span> widthMeasureSpec, <span class="hljs-keyword">int</span> heightMeasureSpec) { <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec); <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(width, height);<span class="hljs-comment">//设置宽和高</span> bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmapCanvas = <span class="hljs-keyword">new</span> Canvas(bitmap); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onDraw</span>(Canvas canvas) { <span class="hljs-keyword">super</span>.onDraw(canvas); <span class="hljs-comment">//绘制Bitmap上的圆形</span> bitmapCanvas.drawCircle(width / <span class="hljs-number">2</span>, height / <span class="hljs-number">2</span>, <span class="hljs-number">150</span>, mPaintCircle); <span class="hljs-comment">//通过Path绘制贝塞尔曲线</span> mPath.reset(); mPath.moveTo(width, (height / <span class="hljs-number">2</span> + <span class="hljs-number">150</span>) - (currentProgress * <span class="hljs-number">300</span>f / maxProgress)); mPath.lineTo(width, height / <span class="hljs-number">2</span> + <span class="hljs-number">200</span>); mPath.lineTo(count, height / <span class="hljs-number">2</span> + <span class="hljs-number">200</span>); mPath.lineTo(count, (height / <span class="hljs-number">2</span> + <span class="hljs-number">150</span>) - (currentProgress * <span class="hljs-number">300</span>f / maxProgress)); <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) { mPath.rQuadTo(<span class="hljs-number">20</span>, <span class="hljs-number">5</span>, <span class="hljs-number">40</span>, <span class="hljs-number">0</span>); mPath.rQuadTo(<span class="hljs-number">20</span>, -<span class="hljs-number">5</span>, <span class="hljs-number">40</span>, <span class="hljs-number">0</span>); } mPath.close(); <span class="hljs-comment">//将贝塞尔曲线绘制到Bitmap的Canvas上</span> bitmapCanvas.drawPath(mPath, mPathPaint); <span class="hljs-comment">//将Bitmap绘制到View的Canvas上</span> bitmapCanvas.drawText(currentProgress * <span class="hljs-number">100</span>f / maxProgress + <span class="hljs-string">"%"</span>, width / <span class="hljs-number">2</span>, height / <span class="hljs-number">2</span>, mPaintText); canvas.drawBitmap(bitmap, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-keyword">null</span>); } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li></ul>
通过这张图片我们可以更好的理解绘制原理。
绘制红色区域的圆形:
<code class="language-java hljs has-numbering"> <span class="hljs-comment">//绘制Bitmap上的圆形</span> bitmapCanvas.drawCircle(width / <span class="hljs-number">2</span>, height / <span class="hljs-number">2</span>, <span class="hljs-number">150</span>, mPaintCircle);</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li></ul>
绘制Path轨迹区域:
注意:这里我们绘制路径是最后使用贝塞尔曲线封闭的。然后Path封闭路径的高度是变化的。
<code class="language-java hljs has-numbering"> <span class="hljs-comment">//通过Path绘制贝塞尔曲线</span> mPath.reset(); mPath.moveTo(width, (height / <span class="hljs-number">2</span> + <span class="hljs-number">150</span>) - (currentProgress * <span class="hljs-number">300</span>f / maxProgress));<span class="hljs-comment">//通过此处根据进度设置高度</span> mPath.lineTo(width, height / <span class="hljs-number">2</span> + <span class="hljs-number">200</span>); mPath.lineTo(count, height / <span class="hljs-number">2</span> + <span class="hljs-number">200</span>); mPath.lineTo(count, (height / <span class="hljs-number">2</span> + <span class="hljs-number">150</span>) - (currentProgress * <span class="hljs-number">300</span>f / maxProgress));<span class="hljs-comment">//通过此处根据进度设置高度</span> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) { mPath.rQuadTo(<span class="hljs-number">20</span>, <span class="hljs-number">5</span>, <span class="hljs-number">40</span>, <span class="hljs-number">0</span>); mPath.rQuadTo(<span class="hljs-number">20</span>, -<span class="hljs-number">5</span>, <span class="hljs-number">40</span>, <span class="hljs-number">0</span>); } mPath.close();</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul>
通过效果,只保留上层的重叠部分:
<code class="language-java hljs has-numbering"> <span class="hljs-comment">//在初始化绘制路径的画笔上加入这个效果</span> mPathPaint.setXfermode(<span class="hljs-keyword">new</span> PorterDuffXfermode(PorterDuff.Mode.SRC_IN));</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li></ul>
控件使用
1. 在布局中定义控件和按钮,点击按钮,进度开始自动增加。
<code class="language-xml hljs has-numbering"><span class="hljs-tag"><<span class="hljs-title">LinearLayout</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span> <span class="hljs-attribute">xmlns:tools</span>=<span class="hljs-value">"http://schemas.android.com/tools"</span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:orientation</span>=<span class="hljs-value">"vertical"</span> <span class="hljs-attribute">tools:context</span>=<span class="hljs-value">"com.example.administrator.mywidgetdemo.activity.MyProgressAnimationActivity"</span>></span> <span class="hljs-tag"><<span class="hljs-title">Button </span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/button_start_myprogressanomation"</span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"wrap_content"</span> /></span> <span class="hljs-tag"><<span class="hljs-title">com.example.administrator.mywidgetdemo.widget.MyProgressAnimation </span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/myprogressanomation"</span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span> /></span> <span class="hljs-tag"></<span class="hljs-title">LinearLayout</span>></span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li></ul>
2. Activity中点击按钮后增加进度。
<code class="language-java hljs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyProgressAnimationActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Activity</span> {</span> <span class="hljs-keyword">private</span> Button mButton; <span class="hljs-keyword">private</span> MyProgressAnimation myprogressanomation; <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> PROGRESS= <span class="hljs-number">0X0003</span>; <span class="hljs-comment">//定义一个进度</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> progress; <span class="hljs-keyword">private</span> Handler handler = <span class="hljs-keyword">new</span> Handler() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg) { <span class="hljs-keyword">super</span>.handleMessage(msg); <span class="hljs-keyword">switch</span> (msg.what) { <span class="hljs-keyword">case</span> PROGRESS: progress++; <span class="hljs-keyword">if</span> (progress <= <span class="hljs-number">100</span>) { myprogressanomation.setCurrentProgress(progress); sendEmptyMessageDelayed(PROGRESS, <span class="hljs-number">100</span>); } <span class="hljs-keyword">break</span>; } } }; <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword">super</span>.onCreate(savedInstanceState); setContentView(R.layout.activity_my_progress_anomation); mButton = (Button) findViewById(R.id.button_start_myprogressanomation); myprogressanomation= (MyProgressAnimation) findViewById(R.id.myprogressanomation); mButton.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) { handler.sendEmptyMessageDelayed(PROGRESS, <span class="hljs-number">1000</span>); } }); } }</code>