首先让我们来看一下需要实现的效果
昨天发现华为加载控件的动画还挺好看的于是就想着自己模仿这做出来。
闲话不多说,看完了效果图接下来就开做!
1.实现原理
我们可以看到其实就是绘制这样一个图案然后利用动画以最中心轴旋转就实现了加载
总共十个圆(这里命名为加载圆)
加载圆1,2,3,4,5大小和颜色一致。
加载圆6,7,8开始大小和颜色依次增加。
加载圆8,10大小和颜色一致。
加载圆9颜色大小最大颜色最深。
这里颜色我们可以通过改变透明度来实现
下面我们通过代码绘制来实现。
2.项目结构
3.自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomLoadingView">
<!--控件颜色-->
<attr name="custom_color" format="color" />
<!--转一圈花费时长-->
<attr name="custom_duration" format="integer" />
<attr name="custom_size" format="dimension"/>
</declare-styleable>
</resources>
4.自定义View
CustomLoadingView.java
public class CustomLoadingView extends View {
private static final int mCount = 10; //加载圆总数
private static final int mRotateValue = 360 / mCount; //每个加载圆圆心间隔角度差
private float[] mAllRadius = new float[mCount]; //记录所有小圆半径
private int[] mAllColors = new int[mCount]; //记录所有小圆颜色
private float mMaxRadius; //小圆最大半径
private int mAnimateValue = 0;
private Paint mPaint; //画笔
private int mSize;
private int mColor;
private long mDuration;
private ValueAnimator mAnimator = null;
public CustomLoadingView(Context context) {
super(context);
}
public CustomLoadingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
initPaint();//初始化画笔
initValue();
}
public CustomLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomLoadingView);
mSize = (int) typedArray.getDimension(R.styleable.CustomLoadingView_custom_size, mSize);
mColor = typedArray.getColor(R.styleable.CustomLoadingView_custom_color, mColor);
mDuration = typedArray.getInt(R.styleable.CustomLoadingView_custom_duration, 1000);
typedArray.recycle();//回收
Log.e("message", String.valueOf(mSize));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mSize, mSize);
}
private void initValue() {
float mMinRadius = mSize / 20;
Log.e("minCircleRadius", String.valueOf(mMinRadius));
for (int i = 0; i < mCount; i++) {//循环从0开始 所以降位
switch (i) {
case 9:
mAllRadius[i] = mMinRadius * 2f;
mAllColors[i] = (int) (255);
break;
case 8:
case 10:
mAllRadius[i] = mMinRadius * 1.75f;
mAllColors[i] = (int) (255 * 0.9f);
break;
case 7:
mAllRadius[i] = mMinRadius * 1.5f;
mAllColors[i] = (int) (255 * 0.8f);
break;
case 6:
mAllRadius[i] = mMinRadius * 1.25f;
mAllColors[i] = (int) (255 * 0.7f);
break;
default:
mAllRadius[i] = mMinRadius;
mAllColors[i] = (int) (255 * 0.5f);
break;
}
}
mMaxRadius = mMinRadius * 2;
}
@Override
protected void onDraw(Canvas canvas) {
if (mSize > 0) {
//mRotateValue * mAnimateValue角度,绘制所有小圆
canvas.rotate(mRotateValue * mAnimateValue, mSize / 2, mSize / 2);//以中心点旋转 直到整体旋转360度
for (int i = 0; i < mCount; i++) {
//设置加载圆透明度
mPaint.setAlpha(mAllColors[i]);
//每隔mRotateValue角度,绘制一个加载圆
canvas.drawCircle(mSize / 2, mMaxRadius, mAllRadius[i], mPaint);//绘制加载圆
canvas.rotate(mRotateValue, mSize / 2, mSize / 2);//旋转加载圆圆心间隔角度差
}
}
}
public void start() {
if (mAnimator == null) {//关闭后还能运行
mAnimator = ValueAnimator.ofInt(0, mCount - 1);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAnimateValue = (int) animation.getAnimatedValue();
postInvalidate();//重绘刷新整个View
}
});
mAnimator.setDuration(mDuration);
mAnimator.setRepeatMode(ValueAnimator.RESTART);
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
} else if (!mAnimator.isStarted()) {
mAnimator.start();
}
}
public void stop() {
if (mAnimator != null) {
mAnimator.removeAllUpdateListeners();
mAnimator.cancel();
mAnimator = null;//设置关闭状态
}
}
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mColor);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.custom.customloadingview.CustomLoadingView
android:id="@+id/customLoadingView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:custom_color="@color/white"
app:custom_size="100dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.385" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在加载..."
android:textSize="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.501"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/customLoadingView"
app:layout_constraintVertical_bias="0.032" />
<Button
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="100dp"
android:text="结束"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_start"
app:layout_constraintTop_toBottomOf="@+id/customLoadingView" />
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="72dp"
android:width="100dp"
android:text="开始"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/customLoadingView"
app:layout_constraintVertical_bias="0.501" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button btn_start;
private Button btn_stop;
private CustomLoadingView customLoadingView;
@Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//版本判断
Window window = getWindow();
// Translucent status bar
window.setFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//设置statusbar应用所占的屏幕扩大到全屏,但是最顶上会有背景透明的状态栏,它的文字可能会盖着你的应用的标题栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start = findViewById(R.id.btn_start);
btn_stop = findViewById(R.id.btn_stop);
customLoadingView=findViewById(R.id.customLoadingView);
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
customLoadingView.start();
}
});
btn_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
customLoadingView.stop();
}
});
}
}
由此就搞定了这个简单的自定义View
附上项目链接
项目链接 欢迎Star
![请输入图片描述][5]