Android实现一个带粘连效果的LoadingBar

前言

我们平时在开发的时候,发起网络请求前,会需要显示一个Loading,一般的做法都是在xml布局上添加好Loading,然后在Activity中,setVisibility来控制Loading的显示和隐藏,这样使用起来就很不方便,因为每一个xml都得引入一个Loading布局。

而LoadingBar就更好的解决了这个问题

最近设计师在外国的一个网站上挑了一个Loading的效果图,尝试实现之后,虽然和原图有点不太一样,但是效果还是不错的。难点就是粘连效果的实现,贝塞尔曲线的点点们简直要把我折磨死了。

先上效果图:


实例代码

然后是源码,就是一个简单VIew,可以直接放在xml中使用。

package top.greendami.greendami;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by GreendaMi on 2017/3/17.
 */
public class PPView extends View {
 String TAG = "PPView";
 //动画开关
 boolean isLoading = true;
 Context mContext;
 private int mWidth = 100;
 private int mheight = 100;
 public int mColor;
 public Paint mPaint = new Paint();
 float time = 0;
 //小球与中间打球的最远距离
 float distance = 100;

 public PPView(Context context) {
  super(context);
  mContext = context;
 }
 public PPView(Context context, @Nullable AttributeSet attrs) {
  super(context, attrs);
  mContext = context;
  mColor = context.getResources().getColor(R.color.colorPrimary);
  init();
 }
 private void init() {
  mPaint.setAntiAlias(true);
  mPaint.setColor(mColor);
 }
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
  int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
  //宽度至少是高度的4倍
  if (widthSpecSize < 4 * heightSpecSize) {
   widthSpecSize = 4 * heightSpecSize;
  }
  mWidth = widthSpecSize;
  mheight = heightSpecSize;
  distance = 1.2f * mheight;
  setMeasuredDimension(widthSpecSize, heightSpecSize);
 }
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  if (isLoading) {
   //大圆半径
   float bigR = mheight * 0.32f + mheight * 0.03f * Math.abs((float) Math.sin(Math.toRadians(time)));
   float smallR = mheight * 0.22f + mheight * 0.03f * Math.abs((float) Math.cos(Math.toRadians(time)));
   float bigx = (getWidth()) / 2;
   //画中间大圆
   canvas.drawCircle(bigx, mheight / 2, bigR, mPaint);
   float smalx = getSmallCenterX();
   //画小圆
   canvas.drawCircle(smalx, mheight / 2, smallR, mPaint);
   //画链接
   //小球在右侧
   if (smalx > bigx) {
    Path path = new Path();
    //上面的贝塞尔曲线的第一个点,在大圆身上
    float x1 = bigx + bigR * (float) Math.cos(Math.toRadians(time));
    float y1 = mheight / 2 - bigR * (float) Math.sin(Math.toRadians(time));
    if (y1 > mheight / 2 - smallR) {
     y1 = mheight / 2 - smallR;
     x1 = bigx + (float) (Math.sqrt(bigR * bigR - smallR * smallR));
    }
    //上面的贝塞尔曲线的第三个点,在小圆身上
    float x2 = smalx - smallR * (float) Math.cos(Math.toRadians(time));
    float y2 = mheight / 2 - smallR * (float) Math.sin(Math.toRadians(time));
    if (y2 > mheight / 2 - smallR * 0.8) {
     y2 = mheight / 2 - smallR * 0.8f;
     x2 = smalx - smallR * (float) (Math.sqrt(1-0.64f));
    }
    //下面的贝塞尔曲线的第三个点,在小圆身上
    float x3 = smalx - smallR * (float) Math.cos(Math.toRadians(time));
    float y3 = mheight / 2 + smallR * (float) Math.sin(Math.toRadians(time));
    if (y3 < mheight / 2 + smallR * 0.8) {
     y3 = mheight / 2 + smallR * 0.8f;
     x3 = smalx - smallR * (float) (Math.sqrt(1-0.64f));
    }
    //下面的贝塞尔曲线的第一个点,在大圆身上
    float x4 = bigx + bigR * (float) Math.cos(Math.toRadians(time));
    float y4 = mheight / 2 + bigR * (float) Math.sin(Math.toRadians(time));
    if (y4 < mheight / 2 + smallR) {
     y4 = mheight / 2 + smallR;
     x4 = bigx + (float) (Math.sqrt(bigR * bigR - smallR * smallR));
    }
    path.moveTo(x1, y1);
    path.quadTo((bigx + smalx) / 2, mheight / 2, x2, y2);
    // 绘制贝赛尔曲线(Path)
    path.lineTo(x3, y3);
    path.quadTo((bigx + smalx) / 2, mheight / 2, x4, y4);
    canvas.drawPath(path, mPaint);
   }
   //小球在左侧
   if (smalx < bigx) {
    Path path = new Path();
    float x1 = bigx + bigR * (float) Math.cos(Math.toRadians(time));
    float y1 = mheight / 2 - bigR * (float) Math.sin(Math.toRadians(time));
    if (y1 > mheight / 2 - smallR) {
     y1 = mheight / 2 - smallR;
     x1 = bigx - (float) (Math.sqrt(bigR * bigR - smallR * smallR));
    }
    float x2 = smalx - smallR * (float) Math.cos(Math.toRadians(time));
    float y2 = mheight / 2 - smallR * (float) Math.sin(Math.toRadians(time));
    if (y2 > mheight / 2 - smallR * 0.8) {
     y2 = mheight / 2 - smallR * 0.8f;
     x2 = smalx + smallR * (float) (Math.sqrt(1-0.64f));
    }
    float x3 = smalx - smallR * (float) Math.cos(Math.toRadians(time));
    float y3 = mheight / 2 + smallR * (float) Math.sin(Math.toRadians(time));
    if (y3 < mheight / 2 + smallR * 0.8) {
     y3 = mheight / 2 + smallR * 0.8f;
     x3 = smalx + smallR * (float) (Math.sqrt(1-0.64f));
    }
    float x4 = bigx + bigR * (float) Math.cos(Math.toRadians(time));
    float y4 = mheight / 2 + bigR * (float) Math.sin(Math.toRadians(time));
    if (y4 < mheight / 2 + smallR) {
     y4 = mheight / 2 + smallR;
     x4 = bigx - (float) (Math.sqrt(bigR * bigR - smallR * smallR));
    }
    path.moveTo(x1, y1);
    path.quadTo((bigx + smalx) / 2, mheight / 2, x2, y2);
    // 绘制贝赛尔曲线(Path)
    path.lineTo(x3, y3);
    path.quadTo((bigx + smalx) / 2, mheight / 2, x4, y4);
    canvas.drawPath(path, mPaint);
   }
   postInvalidate();
  }
 }
 //计算小球的X坐标
 private float getSmallCenterX() {
  //此处控制速度
  time = time + 2.5f;
  return mWidth / 2 + distance * (float) Math.cos(Math.toRadians(time));
 }
}

“精心”画了一张图,对代码做了说明。


在代码中使用

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@color/white" tools:context="top.greendami.greendami.MainActivity">
 <top.greendami.greendami.PPView
  android:layout_centerInParent="true"
  android:layout_width="400dp"
  android:layout_height="80dp" />
</RelativeLayout>

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对呐喊教程的支持。

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#nhooo.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。