模仿360加速球制作一个动态ProgressBar

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>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值