Android自定义View(仿华为加载控件)

首先让我们来看一下需要实现的效果
1

昨天发现华为加载控件的动画还挺好看的于是就想着自己模仿这做出来。
闲话不多说,看完了效果图接下来就开做!

1.实现原理

原理.png

我们可以看到其实就是绘制这样一个图案然后利用动画以最中心轴旋转就实现了加载
总共十个圆(这里命名为加载圆)
加载圆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) {
                /**
                 * 圆1 2 3 4 5 大小透明度一致
                 * 圆6 7 大小透明度增加
                 * 圆9大小透明度最大
                 * 圆10 8 大小一致透明度一致 倒数第二
                 */
                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]

猜你喜欢

转载自blog.csdn.net/qq_35416214/article/details/106207770