Android自定义View (仿计步器)

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

就是一个很简单的自定义View
进入应用圆弧动画过度增加到指定位置并显示步数
话不多说我们开搞

1.项目结构:

2

2.自定义属性

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyStepView">
        <attr name="outerarc_color" format="color"></attr>
        <attr name="innerarc_color" format="color"></attr>
        <attr name="boorder_width" format="dimension"></attr>
        <attr name="steptext_color" format="color"></attr>
        <attr name="steptext_size" format="dimension"></attr>
    </declare-styleable>
</resources>

3.自定义View 实现思路

- 获取自定义属性值
- 测量布局视图大小
- 绘制圆弧文字
- 设置动画
- 简单基础封装一下

上代码中有详细注释:
MyStepView.java 自定义View

public class MyStepView extends View {

    private int mOuterColor;//外圆弧颜色
    private int mInnerColor;//内圆弧颜色
    private int mBorderWidth;//圆弧宽度
    private int mStepTextColor;//显示当前步数颜色
    private int mCurrentStep;//当前步数
    private int mStepTextSize;//显示当前步数文本
    private Paint mOuterPaint;//外圆弧画笔
    private Paint mInnerPaint;//内圆弧画笔
    private int mMaxStep;//目标步数
    private Paint mTextPaint;//文本画笔
    private Paint mTextPaintX;//提示信息画笔
    private int offset = 0;//动画偏移量


    public MyStepView(Context context) {
        super(context);
    }


    public MyStepView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyStepView);//获取自定义属性值
        mOuterColor = array.getColor(R.styleable.MyStepView_outerarc_color, mOuterColor);
        mInnerColor = array.getColor(R.styleable.MyStepView_innerarc_color, mInnerColor);
        mBorderWidth = (int) array.getDimension(R.styleable.MyStepView_boorder_width, 0);
        mStepTextColor = array.getColor(R.styleable.MyStepView_steptext_color, mStepTextColor);
        mStepTextSize = array.getDimensionPixelSize(R.styleable.MyStepView_steptext_size, mStepTextSize);
        array.recycle();//回收
        mOuterPaint = new Paint();
        mOuterPaint.setAntiAlias(true);
        mOuterPaint.setColor(mOuterColor);
        mOuterPaint.setStrokeWidth(mBorderWidth);
        mOuterPaint.setStyle(Paint.Style.STROKE);
        mOuterPaint.setStrokeCap(Paint.Cap.ROUND);

        mInnerPaint = new Paint();
        mInnerPaint.setAntiAlias(true);
        mInnerPaint.setColor(mInnerColor);
        mInnerPaint.setStrokeWidth(mBorderWidth);
        mInnerPaint.setStyle(Paint.Style.STROKE);
        mInnerPaint.setStrokeCap(Paint.Cap.ROUND);


        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(mStepTextColor);
        mTextPaint.setTextSize(mStepTextSize);
        mInnerPaint.setFakeBoldText(true);

        mTextPaintX = new Paint();
        mTextPaintX.setAntiAlias(true);
        mTextPaintX.setColor(mStepTextColor);
        mTextPaintX.setTextSize(70);
        mTextPaintX.setFakeBoldText(true);

    }

    public MyStepView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    /*测量*/
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(width > height ? height : width, width > height ? height : width);
    }


    /*绘制*/
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int center = getWidth() / 2;
        int radius = (getWidth() - mBorderWidth) / 2;
        RectF rectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, center + radius, center + radius);
        canvas.drawArc(rectF, 135, 270, false, mOuterPaint);//绘制内圆弧

        if (mMaxStep == 0) {//如果目标值为0 可以不绘制
            return;
        }
        float radio = (float) offset / mMaxStep;

        canvas.drawArc(rectF, 135, 270 * radio, false, mInnerPaint);//绘制外圆弧
        String mText = offset + "";
        Rect rect = new Rect();
        mTextPaint.getTextBounds(mText, 0, mText.length(), rect);//获取文本框
        int dx = getWidth() / 2 - rect.width() / 2;
        canvas.drawText(mText, dx, getWidth() / 2 + rect.height() / 2, mTextPaint);//绘制文字


        if (offset == mCurrentStep) {//执行完动画 显示当前步数和目标值
            String mTextMax = "今日目标:" + mMaxStep;
            String mTextCurrent = "已完成:" + mCurrentStep;
            Rect rectMaxstep = new Rect();
            Rect rectCurrentStep = new Rect();
            mTextPaintX.getTextBounds(mTextMax, 0, mTextMax.length(), rectMaxstep);//获取文本框
            mTextPaintX.getTextBounds(mTextCurrent, 0, mTextCurrent.length(), rectCurrentStep);//获取文本框
            canvas.drawText(mTextMax, dx - 50, getWidth() + rectMaxstep.height() + 200, mTextPaintX);//绘制文字 今日目标
            canvas.drawText(mTextCurrent, dx - 50, getWidth() + rectCurrentStep.height() + 300, mTextPaintX);//绘制文字 已完成

        }

    }

    /*开始动画*/
    public void startUi() {
        ValueAnimator animator = ObjectAnimator.ofFloat(0, mCurrentStep);//属性动画插值器

        animator.addUpdateListener((animation -> {//lambda表达式
            float animatedValue = (float) animation.getAnimatedValue();
            offset = (int) animatedValue;//实现变化过程
            postInvalidate();//重绘刷新整个View
        }));
        animator.setDuration(3000);//设置动画持续时间
        animator.start();//开始执行动画
    }


    /*设置目标最大值*/
    public void setmMaxStep(int mMaxStep) {
        this.mMaxStep = mMaxStep;
    }

    /*设置当前步数*/
    public void setmCurrentStep(int mCurrentStep) {
        this.mCurrentStep = mCurrentStep;
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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.example.mryan.stepview.MyStepView
        android:id="@+id/myStepView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="20dp"
        app:boorder_width="35dp"
        app:innerarc_color="@color/yellow"
        app:outerarc_color="@color/blue"
        app:steptext_color="@color/white"
        app:steptext_size="70dp" />
</android.support.constraint.ConstraintLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private MyStepView myStepView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);/*不显示标题*/
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);/*全屏设置*/
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//强制竖屏

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//版本判断
            // Translucent status bar
            this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//设置statusbar应用所占的屏幕扩大到全屏,但是最顶上会有背景透明的状态栏,它的文字可能会盖着你的应用的标题栏
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myStepView = findViewById(R.id.myStepView);//绑定
        myStepView.setmMaxStep(1500);//设置最大目标
        myStepView.setmCurrentStep(1314);//设置当前步数
        myStepView.startUi();//开始动画

    }

}

简单完成啦!

附上项目链接

项目链接

猜你喜欢

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