回旧版

最好目录网盘-优质网站推荐分享-以网站收录和网址导航为主的分类目录网!官方网站

一个非常漂亮的自定义Loading,有加载成功和失败两种状态

这还只是张图片

本文由hadisi5216原创,这篇可不能匿名转载。

背景:我一哥们公司做智能设备的,该动画用在手机和家中网络连接时用,他让我看了下需求。刚看到这动画时感觉产品/UI设计的不错,想着试试。昨天开始做的,本来感觉很简单,但做起来貌似没那么简单;最后花了近一天时间终于搞定了。看看效果还行!

niceloading.gif

如果有想直接用的同道中人,看前半部分就行;如果想批评指正我的思考的看看后半部分

1.直接上代码(NiceLoadingView)

package com.hadisi.niceloading;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.util.AttributeSet;import android.view.View;import android.view.animation.LinearInterpolator;/** * Created by hadisi5216 on 2016/7/12. */public class NiceLoadingView extends View {    private Context mContext;    private Paint mPaint;    private int widthSpecSize;    private int heightSpecSize;    private int radiusSmall = 38;    private int radiusbig = 76;    private int moveX;    private int XPoint;    private int mState = -1;//0失败,1成功,-1默认    private boolean mflag;    private ValueAnimator animator;    public NiceLoadingView(Context context) {        super(context);    }    public NiceLoadingView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public NiceLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mContext = context;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        mPaint = new Paint();        mPaint.setColor(0xFFFFBC53);        mPaint.setAntiAlias(true);        if (Math.abs(moveX) > widthSpecSize * 5 / 4)         {            XPoint = (moveX < 0) ? XPoint = widthSpecSize * 7 / 4             - Math.abs(moveX) : widthSpecSize - widthSpecSize * 7 / 4             + Math.abs(moveX);            canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);        }        if (Math.abs(moveX) > widthSpecSize && Math.abs(moveX) < widthSpecSize * 3 / 2)         {            XPoint = (moveX < 0) ? XPoint = widthSpecSize * 3 / 2 - Math.abs(moveX)             : widthSpecSize - widthSpecSize * 3 / 2 + Math.abs(moveX);            canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);        }        if (Math.abs(moveX) > widthSpecSize * 3 / 4 && Math.abs(moveX) < widthSpecSize * 5 / 4)         {            XPoint = (moveX < 0) ? XPoint = widthSpecSize * 5 / 4 - Math.abs(moveX) :             widthSpecSize - widthSpecSize * 5 / 4 + Math.abs(moveX);            canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);        }        if (Math.abs(moveX) > widthSpecSize / 2 && Math.abs(moveX) < widthSpecSize) {            XPoint = (moveX < 0) ? XPoint = widthSpecSize - Math.abs(moveX) :             widthSpecSize - widthSpecSize + Math.abs(moveX);            canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);        }        if (Math.abs(moveX) > widthSpecSize / 4 && Math.abs(moveX) < widthSpecSize * 3 / 4) {            XPoint = (moveX < 0) ? XPoint = widthSpecSize * 3 / 4 - Math.abs(moveX) :             widthSpecSize - widthSpecSize * 3 / 4 + Math.abs(moveX);            canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);        }        if (Math.abs(moveX) > 0 && Math.abs(moveX) < widthSpecSize / 2) {            XPoint = (moveX < 0) ? XPoint = widthSpecSize / 2 - Math.abs(moveX) :             widthSpecSize - widthSpecSize / 2 + Math.abs(moveX);            canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);        }        //中间大圆        if (Math.abs(moveX) > 0 && Math.abs(moveX) < widthSpecSize * 5 / 4) {            radiusbig = 2 * radiusSmall - radiusSmall *             (Math.abs(moveX)) / (widthSpecSize * 5 / 4);            radiusbig = (radiusbig > radiusSmall) ? radiusbig : radiusSmall;            canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);        }        if (Math.abs(moveX) < 12 && mState >= 0) {            if (mState == 0) {                canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);                Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(),                 R.mipmap.connect_failed);                canvas.drawBitmap(bitmap, null, new Rect(widthSpecSize /                 2 - radiusbig, heightSpecSize / 2 - radiusbig, widthSpecSize /                  2 + radiusbig, heightSpecSize / 2 + radiusbig), mPaint);            }            if (mState == 1) {                canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);                Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(),                 R.mipmap.connect_success);                canvas.drawBitmap(bitmap, null, new Rect(widthSpecSize /                 2 - radiusbig, heightSpecSize / 2 - radiusbig, widthSpecSize /                 2 + radiusbig, heightSpecSize / 2 + radiusbig), mPaint);            }        }    }    public void start() {        if (animator != null)            animator.cancel();        moveX = widthSpecSize * (-9 / 4);        mState = -1;        mflag = true;        post(new Runnable() {            @Override            public void run() {                animator = ValueAnimator.ofFloat(0f, 1.0f);                animator.setDuration(3000);//没啥用                animator.setRepeatMode(ValueAnimator.RESTART);                animator.setRepeatCount(ValueAnimator.INFINITE);                animator.setInterpolator(new LinearInterpolator());                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                    @Override                    public void onAnimationUpdate(ValueAnimator animation) {                        if (mState < 0) {                            moveX = (moveX > widthSpecSize * 7 / 4)                             ? widthSpecSize * (-9 / 4) : moveX + 12;                            if (Math.abs(moveX) < 12)                                try {                                    Thread.sleep(1000);                                } catch (InterruptedException e) {                                    e.printStackTrace();                                }                        } else {                            if (moveX > 0)                                moveX = (moveX > widthSpecSize * 7 / 4)                                 ? widthSpecSize * (-9 / 4) : moveX + 12;                            else if (moveX < 0 && mflag) {                                moveX += 12;                                if (Math.abs(moveX) < 12)                                    mflag = false;                            }                        }                        postInvalidate();                    }                });                animator.start();            }        });    }    public void success() {        mState = 1;    }    public void failed() {        mState = 0;    }}

项目已上传到github,戳着

2.怎么用?

布局文件中

<com.hadisi.niceloading.NiceLoadingView          android:id="@+id/nice_loading"          android:layout_width="match_parent"          android:layout_height="100dp" />

你要用的地方

NiceLoadingView niceLoading = (NiceLoadingView) findViewById(R.id.nice_loading);……//开始连接时niceLoading.start();……//连接成功时niceLoading.success();……//连接失败时niceLoading.failed();

3.我怎么实现的!

仔细看效果图可以得出:

1、有6个小圆依次从屏幕左侧移入屏幕中间,然后又依次从屏幕中间移出屏幕右侧。

2、中间有个大圆在随着小球的依次靠近慢慢变大,离开慢慢变小;注意在左侧第一个小圆到达中间时才出现大圆,在最后一个小圆准备向右侧移动时消失;大圆的半径在小圆半径和大圆半径之间。

3、不管何时得到成功和失败的状态,动画终止时都是在小圆依次从左边进入中间后。

4、动画完成后显示成功/失败图片和大圆。

1. 6个小圆的运动

我是这样想的:当第1个小圆移动到widthSpecSize/4(widthSpecSize 为控件的宽度)时第2个小圆开始移动、当第2个小圆移动到widthSpecSize/4时第3个小圆开始移动......当第5个小圆移动到widthSpecSize/4 时第6个小圆开始移动、第6个小圆移动到widthSpecSize/2 时继续移动、当第6个小圆移动到widthSpecSize 3/4时第5个小圆开始移动......当第2个小圆移动到widthSpecSize 3/4时第一个小圆开始移动、最后第1个小球移出屏幕右侧,到此为一个循环。

假设有一个位移变量moveX,moveX在不断增加,其变化范围为(a,b);可以看出按照我的想法,第1个小圆在范围的两边时开始移动、第6个小球在变化范围的中间部分开始移动。

我们可以继续假设变化范围为(-a,a),这样第1个小圆在范围的绝对值大时开始移动、第6个小球在变化范围的绝对值小时开始移动;其实这种重复的动作很容易想到绝对值控制

找张纸画下很容易得到moveX的变化范围在(-widthSpecSize 7/4 , widthSpecSize 7/4)之间。

自己画的图,有点丑

对照图很快可以得出下面代码

if (Math.abs(moveX) > widthSpecSize * 5 / 4) {    XPoint = (moveX < 0) ? XPoint = widthSpecSize * 7 / 4 - Math.abs(moveX) :     widthSpecSize - widthSpecSize * 7 / 4 + Math.abs(moveX);    canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);}if (Math.abs(moveX) > widthSpecSize && Math.abs(moveX) < widthSpecSize * 3 / 2) {    XPoint = (moveX < 0) ? XPoint = widthSpecSize * 3 / 2 - Math.abs(moveX) :     widthSpecSize - widthSpecSize * 3 / 2 + Math.abs(moveX);    canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);}if (Math.abs(moveX) > widthSpecSize * 3 / 4 && Math.abs(moveX) < widthSpecSize * 5 / 4) {    XPoint = (moveX < 0) ? XPoint = widthSpecSize * 5 / 4 - Math.abs(moveX) :     widthSpecSize - widthSpecSize * 5 / 4 + Math.abs(moveX);    canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);}if (Math.abs(moveX) > widthSpecSize / 2 && Math.abs(moveX) < widthSpecSize) {    XPoint = (moveX < 0) ? XPoint = widthSpecSize - Math.abs(moveX) :     widthSpecSize - widthSpecSize + Math.abs(moveX);    canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);}if (Math.abs(moveX) > widthSpecSize / 4 && Math.abs(moveX) < widthSpecSize * 3 / 4) {    XPoint = (moveX < 0) ? XPoint = widthSpecSize * 3 / 4 - Math.abs(moveX) :     widthSpecSize - widthSpecSize * 3 / 4 + Math.abs(moveX);    canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);}if (Math.abs(moveX) > 0 && Math.abs(moveX) < widthSpecSize / 2) {    XPoint = (moveX < 0) ? XPoint = widthSpecSize / 2 - Math.abs(moveX) :     widthSpecSize - widthSpecSize / 2 + Math.abs(moveX);    canvas.drawCircle(XPoint, heightSpecSize / 2, radiusSmall, mPaint);}

中间变化的大圆在左侧第1个小圆到达中间时才出现大圆,在第1个小圆准备向右侧移动时消失,变化范围(-widthSpecSize 5/4 , widthSpecSize 5/4)之间。大圆的半径在小圆半径和大圆半径之间,我们用radiusbig = radiusbig - radiusSmall (Math.abs(moveX)) / (widthSpecSize 5/4)计算大圆半径,可以得到慢慢变大和变小的效果,然后控制在小于radiusSmall时用radiusSmall。

if (Math.abs(moveX) > 0 && Math.abs(moveX) < widthSpecSize * 5 / 4) {  radiusbig = radiusbig - radiusSmall * (Math.abs(moveX)) / (widthSpecSize * 5 / 4);  radiusbig = (radiusbig > radiusSmall) ? radiusbig : radiusSmall;  canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);}

3. 动画终止的控制

正常当一个循环结束时我们需要重新给moveX赋值为widthSpecSize * (-7/4),当收到成功或失败状态时需要判断当前的状态,等到动画进行到结束状态(小圆依次从左边进入中间后)。见下面代码,mState为当前状态(0失败,1成功,-1默认)。

我重新赋值时将moveX设为 widthSpecSize * (-9/4),因为一个循环结束后有点停顿会感觉舒服点,这个无所谓,自己感觉而已

if (mState < 0) {  moveX = (moveX > widthSpecSize * 7 / 4)   ? widthSpecSize * (-9 / 4) : moveX + 12;} else {  if (moveX > 0)      moveX = (moveX > widthSpecSize * 7 / 4)       ? widthSpecSize * (-9 / 4) : moveX + 12;  else if (moveX < 0 && mflag) {      moveX += 12;      if (Math.abs(moveX) < 12)          mflag = false;  }}

4. 显示成功/失败图片

这个简单,在收到成功或失败状态,待动画完成时先画一个大圆,再画一个bitmap

if (Math.abs(moveX) < 12 && mState >= 0) {          if (mState == 0) {              canvas.drawCircle(widthSpecSize / 2,               heightSpecSize / 2, radiusbig, mPaint);              Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.connect_failed);              canvas.drawBitmap(bitmap, null, new Rect(widthSpecSize               / 2 - radiusbig, heightSpecSize / 2 - radiusbig,               widthSpecSize / 2 + radiusbig, heightSpecSize / 2 + radiusbig), mPaint);          }          if (mState == 1) {              canvas.drawCircle(widthSpecSize / 2, heightSpecSize / 2, radiusbig, mPaint);              Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.connect_success);              canvas.drawBitmap(bitmap, null, new Rect(widthSpecSize / 2               - radiusbig, heightSpecSize / 2 - radiusbig, widthSpecSize               / 2 + radiusbig, heightSpecSize / 2 + radiusbig), mPaint);          }      }

5.优化

可以优化,将paint的颜色等属性、大小圆的半径、优化画小圆的逻辑,使小圆个数可变..........

其实核心的就是想法,随便怎么优化。反正我就弄到这了,油而不腻,我觉得挺好,不需要太多优化。吼吼....

此文由 最好目录网盘-优质网站推荐分享-以网站收录和网址导航为主的分类目录网!官方网站 编辑,未经允许不得转载!:首页 > 教程收藏 > 建站经验 » 一个非常漂亮的自定义Loading,有加载成功和失败两种状态

()
分享到:

相关推荐

评论 暂无评论