自定义view-制作一个加载中的圆形

最近因为项目的需求做的一个加载中圆,刚好记录一下
在这里插入图片描述

/**
 * 自定义加载中的圆,主要代码
 */
public class LoadingSearchView extends View {

    private Paint mCirclePaint;
    private Paint mInnerPaint;
    private float mCircleRadius;
    private final static float RADIUS = 180f;
    private final static float INITIAL_ANGLE = 90f;

    /**
     * 旋转角度
     */
    private float mRotationAngle = 0;
    /**
     * 实心半圆最初的位置
     */
    private float mCircleStartAngle;
    /**
     * 实心半圆应该绘制多大的
     */
    private float mCircleSweepAngle;
    /**
     * 文字的画笔
     */
    private Paint mTextPaint;
    /**
     * 外圈圆的宽
     */
    private float mLoadingRoundWidth;
    /**
     * 外圈圆与内圆的间距
     */
    private float mSpacingOfCircles;
    /**
     * 内圆的颜色
     */
    private int mInnerCircleColor;
    /**
     * 设置几个默认的值
     */
    private float mInitialDefaultSize, mLoadingRoundDefaultWidth, mSpacingOfDefaultCircles;

    public LoadingSearchView(Context context) {
        this(context, null);
    }

    public LoadingSearchView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoadingSearchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initDefaultValue();
        initAttributeSet(attrs);
        setCirclePaint();
        initTextPaint();
    }

    /**
     * 初始化一些默认值
     */
    private void initDefaultValue() {
        mCircleStartAngle = INITIAL_ANGLE;
        mLoadingRoundDefaultWidth = dp2px(getContext(), 8);
        mInitialDefaultSize = sp2px(getContext(), 50);
        mSpacingOfDefaultCircles = dp2px(getContext(), 5);
    }

    /**
     * 初始化画笔的颜色
     */
    private void initTextPaint() {
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(sp2px(getContext(), 16));
        mTextPaint.setColor(Color.WHITE);
    }

    /**
     * 初始化圆的画笔
     */
    private void setCirclePaint() {
        mCirclePaint = initCircle();
        mCirclePaint.setStyle(Paint.Style.STROKE);
        mCirclePaint.setStrokeWidth(mLoadingRoundWidth);

        mInnerPaint = initCircle();
        mInnerPaint.setStyle(Paint.Style.FILL);
        mInnerPaint.setColor(mInnerCircleColor);
    }

    private Paint initCircle() {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeJoin(Paint.Join.ROUND);
        return paint;
    }

    private void initAttributeSet(AttributeSet attrs) {
        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.LoadingSearchView);
        mLoadingRoundWidth = array.getDimension(R.styleable.LoadingSearchView_loading_round_width, mLoadingRoundDefaultWidth);
        mSpacingOfCircles = array.getDimension(R.styleable.LoadingSearchView_spacing_of_circles, mSpacingOfDefaultCircles);
        float defaultCircle = mLoadingRoundDefaultWidth + mSpacingOfDefaultCircles + mInitialDefaultSize;
        mCircleRadius = array.getDimension(R.styleable.LoadingSearchView_loading_circle_radius, defaultCircle);
        mInnerCircleColor = array.getColor(R.styleable.LoadingSearchView_inner_circle_color, Color.parseColor("#00d8c9"));
        array.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int measuredWidth = getMeasuredWidth();
        float digitalDifference = (measuredWidth - mCircleRadius * 2) / 2;

        if (digitalDifference < 0) {
            setMeasuredDimension((int) (mCircleRadius * 2), (int) (mCircleRadius * 2));
        }

        drawOuterCircle(digitalDifference, canvas);

        drawInnerCircle(digitalDifference, canvas);

        drawSuccessAnimator(digitalDifference, canvas);

    }

    private void drawSuccessAnimator(float digitalDifference, Canvas canvas) {
        if (mCircleSweepAngle >= 360) {
            String success = "成功";
            Rect bounds = new Rect();
            mTextPaint.getTextBounds(success, 0, success.length(), bounds);
            float startX = mCircleRadius - bounds.right / 2;
            Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
            int dy = (fontMetricsInt.top - fontMetricsInt.bottom) / 2 - fontMetricsInt.top;
            float baseLine = mCircleRadius + dy;
            canvas.drawText(success, startX + digitalDifference, baseLine + digitalDifference, mTextPaint);
        }
    }

    private void drawInnerCircle(float digitalDifference, Canvas canvas) {
        RectF circleRectF = new RectF(mLoadingRoundWidth + mSpacingOfCircles + digitalDifference, mLoadingRoundWidth + mSpacingOfCircles + digitalDifference,
                (mCircleRadius * 2 - (mLoadingRoundWidth + mSpacingOfCircles)) + digitalDifference, (mCircleRadius * 2 - (mLoadingRoundWidth + mSpacingOfCircles)) + digitalDifference);
        canvas.drawArc(circleRectF, mCircleStartAngle, mCircleSweepAngle, false, mInnerPaint);
    }

    /**
     * 画外圆
     *
     * @param digitalDifference
     * @param canvas
     */
    private void drawOuterCircle(float digitalDifference, Canvas canvas) {
        RectF rectF = new RectF(mLoadingRoundWidth / 2 + digitalDifference, mLoadingRoundWidth / 2 + digitalDifference,
                (mCircleRadius * 2 - mLoadingRoundWidth / 2) + digitalDifference, (mCircleRadius * 2 - mLoadingRoundWidth / 2) + digitalDifference);
        mCirclePaint.setColor(Color.parseColor("#00d8c9"));
        canvas.drawArc(rectF, mRotationAngle, RADIUS, false, mCirclePaint);
        mCirclePaint.setColor(Color.parseColor("#64e27f"));
        float rotationAngle = mRotationAngle + RADIUS;
        canvas.drawArc(rectF, rotationAngle, RADIUS, false, mCirclePaint);
    }

    public void setRotationAngle(float rotationAngle) {
        mRotationAngle = mRotationAngle + 3;
        float angle = RADIUS * rotationAngle;
        mCircleStartAngle = INITIAL_ANGLE - angle;
        mCircleSweepAngle = angle * 2;
        invalidate();
    }

    /**
     * dp转px
     */
    public float dp2px(Context context, float dpVal) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal, context.getApplicationContext().getResources().getDisplayMetrics());
    }

    /**
     * sp转px
     */
    public int sp2px(Context context, float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, context.getApplicationContext().getResources().getDisplayMetrics());
    }
}

自定义的属性如下,在values文件中创建文件attrs.xml:

<declare-styleable name="LoadingSearchView">
        <attr name="loading_circle_radius" format="dimension"/>
        <attr name="loading_round_width" format="dimension"/>
        <attr name="spacing_of_circles" format="dimension"/>
        <attr name="inner_circle_color" format="color"/>
    </declare-styleable>

activity页面的布局也是只有一个加载的view,如下:

<LinearLayout
    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"
    android:gravity="center"
    tools:context=".MainActivity">

    <com.lift.ajun.testapplication.LoadingSearchView
        android:id="@+id/test_view"
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:loading_round_width="10dp"/>

</LinearLayout>

MainActivity里面的代码如下:

public class MainActivity extends AppCompatActivity {

    private LoadingSearchView mTestView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTestView = findViewById(R.id.test_view);
        mTestView.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
            	// 创建一个属性动画,来启动圆的动画
                ObjectAnimator animator = ObjectAnimator.ofFloat(mTestView, "rotationAngle", 1f);
                animator.setInterpolator(new DecelerateInterpolator());
                animator.setDuration(4000);
                animator.start();
            }
        });
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值