您的位置:首页 > 移动开发 > Android开发

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

2016-10-24 19:30 627 查看
话说今天就是传说中的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送你一个漂亮的加载进度条。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: