自下而上的炫酷进度条 --- Android自定义组件进阶版

效果展示

自下而上的炫酷进度条效果展示
自下而上的炫酷进度条效果展示

 源码分析

第一步:创建自定义组件类

第二步:自定义属性文件编写  

<declare-styleable name="myLoadingBottomTop">
        <attr name="circleNormalColor" format="color"/>
        <attr name="circleChangeColor" format="color"/>
        <attr name="circleRadius" format="dimension"/>

        <attr name="loadingText" format="string"/>
        <attr name="loadingTextSize" format="dimension"/>
        <attr name="loadingTextNormalColor" format="color"/>
        <attr name="loadingTextChangeColor" format="color"/>
    </declare-styleable>

 第三步:在布局文件中的引用

<com.wustyq.senseone.selfView.myLoadingBottomTop
        android:id="@+id/lb_myLoadingBottomTop"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_margin="10dp"
        app:circleNormalColor="#ccdefc"
        app:circleChangeColor="#4a8af4"
        app:circleRadius="20dp"
        app:loadingText="壹"
        app:loadingTextSize="16sp"
        app:loadingTextNormalColor="#4a8af4"
        app:loadingTextChangeColor="#ccdefc"/>
    <EditText
        android:id="@+id/et_upDateText"
        android:layout_width="match_parent"
        android:layout_height="50dp"/>
    <Button
        android:id="@+id/bt_test_three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="第三个组件"/>

第四步:在自定义组件类里获取属性并编写相关代码

package com.wustyq.senseone.selfView;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import androidx.annotation.Nullable;

import com.wustyq.senseone.R;

public class myLoadingBottomTop extends View {

    private int mCircleNormalColor;
    private int mCircleChangeColor;
    private int mCircleRadius; //这里是dp2px

    private String mLoadingText;
    private int mLoadingTextSize; //这里sp2px
    private int mLoadingTextNormalColor;
    private int mLoadingTextChangeColor;

    private Paint mCircleNormalPaint;
    private Paint mCircleChangePaint;
    private Paint mLoadingTextNormalPaint;
    private Paint mLoadingTextChangePaint;

    private float mCurrentProgress = 0.0f;

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

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

    public myLoadingBottomTop(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myLoadingBottomTop);
        mCircleNormalColor = typedArray.getColor(R.styleable.myLoadingBottomTop_circleNormalColor,mCircleNormalColor);
        mCircleChangeColor = typedArray.getColor(R.styleable.myLoadingBottomTop_circleChangeColor,mCircleChangeColor);
        mCircleRadius = (int) typedArray.getDimension(R.styleable.myLoadingBottomTop_circleRadius, dp2px(mCircleRadius));

        mLoadingText = typedArray.getString(R.styleable.myLoadingBottomTop_loadingText);
        mLoadingTextSize = typedArray.getDimensionPixelSize(R.styleable.myLoadingBottomTop_loadingTextSize, (int) sp2px(mLoadingTextSize));
        mLoadingTextNormalColor = typedArray.getColor(R.styleable.myLoadingBottomTop_loadingTextNormalColor,mLoadingTextNormalColor);
        mLoadingTextChangeColor = typedArray.getColor(R.styleable.myLoadingBottomTop_loadingTextChangeColor,mLoadingTextChangeColor);

        //初始化画笔
        mCircleNormalPaint = initCirclePaintByColor(mCircleNormalColor);
        mCircleChangePaint = initCirclePaintByColor(mCircleChangeColor);
        mLoadingTextNormalPaint = initTextPaintByColor(mLoadingTextNormalColor);
        mLoadingTextChangePaint = initTextPaintByColor(mLoadingTextChangeColor);

        typedArray.recycle();
    }

    private Paint initCirclePaintByColor(int color) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setColor(color);
        return paint;
    }

    private Paint initTextPaintByColor(int color) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setColor(color);
        paint.setTextSize(mLoadingTextSize);
        return paint;
    }

    private float dp2px(int dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }

    private float sp2px(int sp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        setMeasuredDimension(Math.min(width,height),Math.min(width,height));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCircleText(canvas,mCircleNormalPaint,mLoadingTextNormalPaint,0,(int) (getHeight()*(1-mCurrentProgress)));
        drawCircleText(canvas,mCircleChangePaint,mLoadingTextChangePaint,getHeight(),(int) (getHeight()*(1-mCurrentProgress)));
    }


    private void drawCircleText(Canvas canvas, Paint circlePaint, Paint textPaint, int start, int end) {
        canvas.save();
        Rect rect = new Rect(0,start,getWidth(), end);
        canvas.clipRect(rect);
        canvas.drawCircle(getWidth()/2,getHeight()/2,mCircleRadius,circlePaint);
        //获取文字基线
        int baseLine = getTextBaseLine();
        //获取文字的宽度/高度
        int textWidth = getTextWidth(mLoadingText);
        textPaint.setTextSize(mLoadingTextSize);
        canvas.drawText(mLoadingText,getWidth()/2 - textWidth/2,baseLine,textPaint);
        canvas.restore();
    }

    private int getTextWidth(String str) {
        Rect bounds = new Rect();
        mLoadingTextNormalPaint.getTextBounds(str,0,str.length(),bounds);
        return bounds.width();
    }

    private int getTextHeight(String str) {
        Rect bounds = new Rect();
        mLoadingTextNormalPaint.getTextBounds(str,0,str.length(),bounds);
        return bounds.height();
    }

    private int getTextBaseLine() {
        Paint.FontMetricsInt fontMetricsInt = mLoadingTextChangePaint.getFontMetricsInt();
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom;
        return getHeight()/2 + dy;
    }

    //对外暴露一些方法
    public void setCurrentProgress(float currentProgress) {
        this.mCurrentProgress = currentProgress;
        invalidate();
    }
    public void setLoadingText(String str){
        if (TextUtils.isEmpty(str)) return;
        this.mLoadingText = str;
    }
}

第五步:在外面调用

package com.wustyq.senseone.app;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.View;
import android.view.animation.BounceInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;

import com.wustyq.senseone.R;
import com.wustyq.senseone.selfView.myLoading;
import com.wustyq.senseone.selfView.myLoadingBottomTop;
import com.wustyq.senseone.selfView.myProgress;

public class MainActivity extends Activity implements View.OnClickListener {

    private myProgress mp_myProgress;
    private myLoading ml_myLoading;
    private Button bt_test_one;
    private Button bt_test_two;
    private int clickNum = 0;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            ml_myLoading.exChange();
            handler.sendEmptyMessageDelayed(0, 1000);
        }
    };
    ;
    private Handler handler2 = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0.0f, 1.0f);
            valueAnimator.setDuration(4000);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    lb_myLoadingBottomTop.setCurrentProgress(value);
                }
            });
            valueAnimator.start();
            handler2.sendEmptyMessageDelayed(0, 4000);
        }
    };
    private myLoadingBottomTop lb_myLoadingBottomTop;
    private Button bt_test_three;
    private EditText et_upDateText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initWelcome();
        initView();
        initData();
    }

    private void initView() {
        mp_myProgress = findViewById(R.id.mp_myProgress);
        ml_myLoading = findViewById(R.id.ml_myLoading);
        bt_test_one = findViewById(R.id.bt_test_one);
        bt_test_two = findViewById(R.id.bt_test_two);
        lb_myLoadingBottomTop = findViewById(R.id.lb_myLoadingBottomTop);
        bt_test_three = findViewById(R.id.bt_test_three);
        et_upDateText = findViewById(R.id.et_upDateText);
    }

    private void initData() {
        bt_test_one.setOnClickListener(this);
        bt_test_two.setOnClickListener(this);
        bt_test_three.setOnClickListener(this);
    }

    private void initWelcome() {
        Toast toast = Toast.makeText(this, null, Toast.LENGTH_SHORT);
        toast.setText("欢迎来到第一感知,很高兴为您服务");
        toast.setGravity(Gravity.CENTER, 0, 0);
        toast.show();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_test_one: {
                System.out.println(clickNum + "clickNum");
                if (clickNum == 0) {
                    setValueObject(mp_myProgress, 0.0f, 0.25f);
                    clickNum++;
                } else if (clickNum == 1) {
                    setValueObject(mp_myProgress, 0.25f, 0.5f);
                    clickNum++;
                } else if (clickNum == 2) {
                    setValueObject(mp_myProgress, 0.5f, 0.75f);
                    clickNum++;
                } else if (clickNum == 3) {
                    setValueObject(mp_myProgress, 0.75f, 1.0f);
                    clickNum = 0;
                }
            }
            break;
            case R.id.bt_test_two: {
                handler.removeCallbacksAndMessages(null);
                handler.sendEmptyMessage(0);
            }
            break;
            case R.id.bt_test_three: {
                lb_myLoadingBottomTop.setLoadingText(et_upDateText.getText().toString());
                handler2.removeCallbacksAndMessages(null);
                handler2.sendEmptyMessage(0);
            }
            break;
        }
    }

    private void setValueObject(final myProgress view, float start, float end) {
        ValueAnimator valueAnimator = ObjectAnimator.ofFloat(start, end);
        valueAnimator.setDuration(2000);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float animatedValue = (float) animation.getAnimatedValue();
                view.setCurrentProgress(animatedValue);
            }
        });
        valueAnimator.start();
    }
}

总结

大家发现,越往后面的文章,写的都比较简介,因为前面我们每个方法都详细讲过了,相信大家都有基础了,再讲哪些就是凑字数了,没必要。基础不行的同志请自行复习。

注意

在获取文字 宽高 和基线的时候,如果发现获取的值不准确,文字出现了偏移,大部分原因是 代码顺序有问题,详细说明请看 获取文字基线和文字宽度不准确原因分析

 

猜你喜欢

转载自blog.csdn.net/qq_41885673/article/details/114260559