您的位置:首页 > 移动开发 > Android开发

android 仿易讯客户端loading效果

2015-08-06 09:38 591 查看
下面来实现一个loading效果。具体效果如下:




首先对这个效果进行拆分,它由以下部分组成:

1 一个”闪电”样式的图案;
2 “闪电”图案背后是一个圆角矩形;
3 “闪电”图案上面有一层颜色不断”飘过”

拆分完效果后,思考下如何实现。下面是我的思考过程。

1 android sdk并没有提供这样的控件,很显然是需要自定义控件;
2 很显然是一个View而不是ViewGroup,所以可以继承View;
3 重点是onDraw的逻辑;
4 怎样绘制”闪电”的图案?可以通过Path绘制;
5 怎样绘制”闪电”背后的圆角矩形?canvas.drawRoundRect;
6 怎样实现”闪电”的动效?仔细观察,发现上方那层颜色的运动规律是从0~闪电高度不断扩大,到达闪电高度的时候,高度不断减小直到0。所以可以通过控制高度的方式实现。只要有两个变量scanTop/scanBottom记录绘制的上下界限即可,然后控制scanTop/scanBottom进行变化即可,怎样控制变化很显然可以通过post/postDelayed实现。另外一个难点是如何绘制部分”闪电”?思索一番,可以通过canvas.clipRect的方式控制绘制区域,这样间接实现了我们需要的效果;
7 核心逻辑实现之后,需要考虑到应该让这个自定义控件支持wrap_content.这必然需要重写onMeasure,并考虑到父容器的MeasureSpec(view的默认实现下wrap_content和match_parent效果一样);
8 需要让这个控件支持padding。所以得在measure和draw的过程中充分考虑到padding这个因素;
9 当view被detach的时候,需要remove掉动画;
10 应该提供几个默认大小,比如small/midium/large,这可以通过自定义属性实现。

大致思考完之后,可以写代码了。

首先是measure过程:
<code class="hljs java has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onMeasure</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> widthMeasureSpec, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> heightMeasureSpec) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//需要计算自己实际需要的宽高</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//需要把padding考虑进来</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//需要考虑父容器的测量规则</span>

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> width,height;

        width = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>)mViewMinWidth+getPaddingLeft()+getPaddingRight();
        height = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>)mViewMinHeight+getPaddingTop()+getPaddingBottom();

        setMeasuredDimension(getMeasuredSize(widthMeasureSpec, width), getMeasuredSize(heightMeasureSpec, height));
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul>


通过getMeasuredSize计算考虑父容器限制后的实际大小:
<code class="hljs mel has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">private <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> getMeasuredSize(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> measureSpec,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> desiredSize){

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> result;

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> mode = MeasureSpec.getMode(measureSpec);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">size</span> = MeasureSpec.getSize(measureSpec);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">switch</span> (mode){
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> MeasureSpec.EXACTLY:
                result = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">size</span>;
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">default</span>:
                result = desiredSize;
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(mode == MeasureSpec.AT_MOST)
                    result = Math.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">min</span>(result,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">size</span>);
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> result;

    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li></ul>


然后是draw的过程:
<code class="hljs mel has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">
    <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">@Override</span>
    protected void onDraw(Canvas <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">canvas</span>) {
        super.onDraw(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">canvas</span>);

        mPaint.setColor(mViewBackground);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果xml中设置layout_width/layout_height大于默认宽高,那么居中(不允许小于默认宽高)</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(getWidth()-getPaddingLeft()-getPaddingRight() > (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>)mViewMinWidth || getHeight()-getPaddingTop()-getPaddingBottom() > (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>)mViewMinHeight){
           <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">canvas</span>.translate((getWidth()-mViewMinWidth)/<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2.0</span>f,(getHeight()-mViewMinHeight)/<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2.0</span>f);
        }
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//画圆角矩形</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">canvas</span>.drawRoundRect(mBounds, dp2px(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5</span>), dp2px(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5</span>), mPaint);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//平移到圆角矩形中心点,画闪电</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">canvas</span>.translate((mViewMinWidth - mDefaultWidth) / <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2.0</span>f, (mViewMinHeight - mDefaultHeight) / <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2.0</span>f);
        mPaint.setColor(mBackgroundColor);

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">canvas</span>.drawPath(mThunderPath, mPaint);
        mPaint.setColor(mCoverColor);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//通过clicpRect的方式控制可绘制区域(在外界看来好像有闪动的动画效果)</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">canvas</span>.clipRect(getPaddingLeft(), mScanTop + getPaddingTop(), mDefaultWidth + getPaddingLeft(), mScanBottom + getPaddingTop());
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">canvas</span>.drawPath(mThunderPath, mPaint);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li></ul>


mScanTop/mScanBottom变量可以通过post()进行改变:
<code class="hljs axapta has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">AnimRunnable</span> <span class="hljs-inheritance" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">implements</span></span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Runnable</span>{</span>
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> run() {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!flag) {
                mScanBottom += mGap;
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mScanBottom >= mDefaultHeight) {
                    mScanBottom = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>) mDefaultHeight;
                    flag = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
                }
                postInvalidate();
                post(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);
            } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
                mScanTop += mGap;
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mScanTop >= mDefaultHeight) {
                    mScanTop = mScanBottom = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
                    flag = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
                    postInvalidate();
                    postDelayed(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">700</span>);
                } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
                    postInvalidate();
                    post(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);
                }
            }
        }
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
<code class="hljs cs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">startAnim</span>() {
        mRunnable = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> AnimRunnable();
        post(mRunnable);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>


核心代码就这么多。

完整代码在这里:https://github.com/Rowandjj/ThunderLoadingView
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: