Android自定义之仿支付宝支付成功、失败状态的加载进度

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaxiazaizai01/article/details/52914558

话说今天就是传说中的1024,程序员的节日,在这个重大的节日里我决定撸一篇博文压压惊。今天实现的效果是仿支付宝支付时的几种状态效果,首先是Material Design风格的小圆圈在不停的旋转,当支付成功后,会出现一个带动画效果的对勾,反之,一个大红叉子呈现在你面前,这就尴尬了。。
先看下效果图
这里写图片描述
对于自定义view前面也写了很多篇了,这里就不再啰嗦了,关于自定义view的步骤请移驾查看前几篇博文,理论+实践,相信你能有所收获。

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MdStyleProgress">
        <attr name="progress_color" format="color"/>
        <attr name="progress_width" format="dimension"/>
        <attr name="radius" format="dimension"/>
    </declare-styleable>
</resources>

还是把整个自定义view的类贴出来吧,方便大家浏览,其中对勾和叉号动画,我采用的是属性动画来实现的,并通过AnimatorSet实现将多个动画组合到一起,关于属性动画相关的知识点,可以到郭婶的博客中去,写的比较详细,什么??你不愿意去翻,那好吧,我把地址贴过来吧,郭婶讲属性动画

public class MdStyleProgress extends View {

    private static final int PROGRESS_COLOR = Color.parseColor("#10a679");
    private static final int PROGRESS_WIDTH = 3;
    private static final int RADIUS = 30;

    private int mProgressColor = PROGRESS_COLOR;
    private int mProgressWidth = dp2px(PROGRESS_WIDTH);
    private int mRadius = dp2px(RADIUS);

    private Paint progressPaint;

    private int rotateDelta = 4;
    private int curAngle = 0;

    private int minAngle = -90;
    private int startAngle = -90;
    private int endAngle = 120;

    private Path path;
    private Status mStatus = Status.Loading;
    private float lineValueLeft;//左边对勾
    private float lineValueRight;//右边对勾
    private float failLineFirst;//叉号
    private float failLineSecond;

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

    public MdStyleProgress(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public MdStyleProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取自定义属性
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MdStyleProgress);
        int indexCount = typedArray.getIndexCount();
        for (int i=0;i<indexCount;i++){
            int attr = typedArray.getIndex(i);
            switch (attr){
                case R.styleable.MdStyleProgress_progress_color:
                    mProgressColor = typedArray.getColor(attr,PROGRESS_COLOR);
                    break;
                case R.styleable.MdStyleProgress_progress_width:
                    mProgressWidth = (int) typedArray.getDimension(attr,mProgressWidth);
                    break;
                case R.styleable.MdStyleProgress_radius:
                    mRadius = (int) typedArray.getDimension(attr,mRadius);
                    break;
            }
        }
        //回收TypedArray对象
        typedArray.recycle();
        //设置画笔
        setPaint();

        path = new Path();
        path.moveTo(mRadius/2,mRadius);
        path.lineTo(mRadius,mRadius+mRadius/2);
        path.lineTo(mRadius+mRadius/2,mRadius/2);
    }

    private void setPaint() {
        progressPaint = new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setDither(true);
        progressPaint.setColor(mProgressColor);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(mProgressWidth);
        progressPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int widthSize;
        int heightSize;
        if(widthMode != MeasureSpec.EXACTLY){
            widthSize = getPaddingLeft() + mProgressWidth + mRadius*2 + getPaddingRight();
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize,MeasureSpec.EXACTLY);
        }
        if(heightMode != MeasureSpec.EXACTLY){
            heightSize = getPaddingTop() + mProgressWidth + mRadius*2 + getPaddingBottom();
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize,MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

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

        canvas.save();
        canvas.translate(getPaddingLeft(),getPaddingTop());

        if(mStatus == Status.Loading){
            if (startAngle == minAngle) {
                endAngle += 6;
            }
            if (endAngle >= 300 || startAngle > minAngle) {
                startAngle += 6;
                if(endAngle > 20) {
                    endAngle -= 6;
                }
            }
            if (startAngle > minAngle + 300) {
                minAngle = startAngle;
                endAngle = 20;
            }
            canvas.rotate(curAngle += rotateDelta,mRadius,mRadius);//旋转rotateDelta=4的弧长
            canvas.drawArc(new RectF(0,0,mRadius*2,mRadius*2),startAngle,endAngle,false,progressPaint);
            invalidate();
        }else if(mStatus == Status.LoadSuccess){
            canvas.drawArc(new RectF(0,0,mRadius*2,mRadius*2),startAngle,360,false,progressPaint);
            //canvas.drawPath(path,progressPaint);
            //画圆圈中的对勾
            canvas.drawLine(mRadius/2,mRadius,mRadius/2+lineValueLeft,mRadius+lineValueLeft,progressPaint);
            canvas.drawLine(mRadius,mRadius+mRadius/2,mRadius+lineValueRight,mRadius+mRadius/2-1.5f*lineValueRight,progressPaint);
        }else {
            canvas.drawArc(new RectF(0,0,mRadius*2,mRadius*2),startAngle,360,false,progressPaint);
            //画圆圈中的叉号(画右边叉号的时候,终止位置的x坐标为起始x位置减去failLineFirst,而failLineFirst在属性动画中的取值范围是0-mRadius)
            canvas.drawLine(mRadius+mRadius/2,mRadius/2,mRadius*3/2-failLineFirst,mRadius/2+failLineFirst,progressPaint);
            canvas.drawLine(mRadius/2,mRadius/2,mRadius/2+failLineSecond,mRadius/2+failLineSecond,progressPaint);
        }

        canvas.restore();
    }


    public enum Status{
        Loading,
        LoadSuccess,
        LoadFail
    }

    public void startAnima(){
        //对勾左边线的属性动画
        ValueAnimator animatorLeft = ValueAnimator.ofFloat(0f,mRadius/2f);
        animatorLeft.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
               lineValueLeft = (float) animation.getAnimatedValue();
                Log.i("lineValueLeft",lineValueLeft+"");
                invalidate();//重绘,调onDraw()重绘
            }
        });
        //对勾右边线的属性动画
        ValueAnimator animatorRight = ValueAnimator.ofFloat(0f,mRadius/2f);
        animatorRight.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                lineValueRight = (float) animation.getAnimatedValue();
                Log.i("lineValueRight",lineValueRight+"");
                invalidate();
            }
        });
        //将多个动画组合到一起需要借助AnimatorSet这个类
        final AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(animatorRight).after(animatorLeft);
        animatorSet.setDuration(150);
        animatorSet.start();
    }

    public void failAnima(){
        ValueAnimator failOne = ValueAnimator.ofFloat(0f,mRadius);
        failOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                failLineFirst = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        ValueAnimator failOther = ValueAnimator.ofFloat(0f,mRadius);
        failOther.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                failLineSecond = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(failOther).after(failOne);
        animatorSet.setDuration(150);
        animatorSet.start();
    }

    public Status getStatus() {
        return mStatus;
    }

    public void setStatus(Status mStatus) {
        this.mStatus = mStatus;
        invalidate();
    }

    /**
     * dp 2 px
     */
    protected int dp2px(int dpVal)
    {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal, getResources().getDisplayMetrics());
    }
    /**
     * sp 2 px
     */
    protected int sp2px(int spVal)
    {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, getResources().getDisplayMetrics());
    }
}

xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.mdstyleprogress.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="30dp"
        android:orientation="horizontal">
        <!--<Button
            android:id="@+id/loading"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:text="还原"
            android:layout_marginRight="5dp"
            android:layout_weight="1"/>-->
        <Button
            android:id="@+id/success"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:text="成功"
            android:layout_marginRight="5dp"
            android:layout_weight="1"/>
        <Button
            android:id="@+id/fail"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:text="失败"
            android:layout_weight="1"/>
    </LinearLayout>
    <com.example.mdstyleprogress.MdStyleProgress
        android:id="@+id/progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:padding="10dp"
         />
</RelativeLayout>

最后是我们的MainActivity调用

public class MainActivity extends AppCompatActivity {

    private MdStyleProgress progress;
    private Button btnLoading,btnSuccess,btnFail;

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

        progress = (MdStyleProgress) findViewById(R.id.progress);
        //btnLoading = (Button) findViewById(R.id.loading);
        btnSuccess = (Button) findViewById(R.id.success);
        btnFail = (Button) findViewById(R.id.fail);
        /*btnLoading.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(progress.getStatus() != MdStyleProgress.Status.Loading){
                    progress.setStatus(MdStyleProgress.Status.Loading);
                }
            }
        });*/
        //成功状态
        btnSuccess.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(progress.getStatus() != MdStyleProgress.Status.LoadSuccess){
                    progress.setStatus(MdStyleProgress.Status.LoadSuccess);
                    progress.startAnima();
                }
            }
        });

        //失败状态
        btnFail.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(progress.getStatus() != MdStyleProgress.Status.LoadFail){
                    progress.setStatus(MdStyleProgress.Status.LoadFail);
                    progress.failAnima();
                }
            }
        });
    }
}

代码中已经注释的很清楚了,其中叉号的实现不是很漂亮,略显长,可适当调整下叉号的坐标值即可,这里我就不再微调了,关于坐标的计算如果有看不懂的,欢迎提出,也可以自己下载demo,断点调试下就明白了,随后即将源码奉上。1024送你一个漂亮的加载进度条。

猜你喜欢

转载自blog.csdn.net/xiaxiazaizai01/article/details/52914558