package com.loaderman.customviewdemo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View; import static com.loaderman.customviewdemo.AlipayView.State.FINISH;
import static com.loaderman.customviewdemo.AlipayView.State.IDLE; /**
* Created by MQ on 2017/1/20.
*/ public class AlipayView extends View {
private Paint mPaint;
private RectF mRectF;
private int mCenterX, mCenterY;
private State state = IDLE;
private int firstProgress;//第一个圆弧进度
private int secondProgress;//第二个圆弧进度
private Path mPath;
private boolean isEndPay;//是否支付完成
private Line firstLine;//对勾第一条线
private Line secondLine;//对勾第二条线
private float lineProgress;//打对勾的进度
private int sweepLength;//正在支付状态的圆弧长度
private float xDis3to1, xDis2to1;//xDis3to1为对勾的横向长度 xDis2to1为第二个坐标到第一个坐标的横向长度
private float x1, y1, x2, y2, x3, y3;//构成对勾的3个坐标(x1,y1)(x2,y2)(x3,y3) private int radius = 150;//旋转圆的半径(修改此值可以改变圆的大小)
private float increaseDis = 10f;//对勾增长速率(修改此值可以改变打对勾的速率)
private float circleIncRate = 10f;//圆圈增长速率(修改此值可以改变画圆的速率)
private int sweepMaxAngle = 200;//正在支付状态的最大圆弧长度(修改此值可以改变最大圆弧的长度) public AlipayView(Context context) {
this(context, null);
} public AlipayView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public AlipayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} private void init() {
//初始化画笔
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(10f);
mPaint.setColor(getResources().getColor(R.color.holo_blue_light));
//绘制范围
mRectF = new RectF();
mPath = new Path();
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenterX = w / 2;
mCenterY = h / 2;
x1 = mCenterX - radius / 3 * 2;
y1 = mCenterY + radius / 8;
x2 = mCenterX - radius / 5;
y2 = mCenterY + radius / 3 * 2;
x3 = mCenterX + radius / 4 * 3;
y3 = mCenterY - radius / 4;
firstLine = new Line(new PointF(x1, y1), new PointF(x2, y2));
secondLine = new Line(new PointF(x2, y2), new PointF(x3, y3));
xDis3to1 = x3 - x1;
xDis2to1 = x2 - x1;
} @Override
protected void onDraw(Canvas canvas) {
mRectF.set(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + radius);
switch (state) {
case IDLE:
canvas.drawArc(mRectF, -90f, 360f, false, mPaint);
mPath.moveTo(firstLine.startPoint.x, firstLine.startPoint.y);
mPath.lineTo(firstLine.endPoint.x, firstLine.endPoint.y);
mPath.lineTo(secondLine.endPoint.x, secondLine.endPoint.y);
canvas.drawPath(mPath, mPaint);
break;
case PROGRESS:
//支付中
canvas.drawArc(mRectF, -90f + firstProgress, sweepLength, false, mPaint);
secondProgress += circleIncRate;
if (secondProgress < sweepMaxAngle) {
firstProgress = 0;
sweepLength += circleIncRate;
} else if (secondProgress >= sweepMaxAngle && secondProgress <= 360) {
firstProgress = secondProgress - sweepMaxAngle;
sweepLength = sweepMaxAngle;
} else if (secondProgress > 360) {
if (sweepLength > 0) {
firstProgress += circleIncRate;
sweepLength -= circleIncRate;
} else {
// canvas.drawArc(mRectF, -89f, 1f, false, mPaint);
reset();
if (isEndPay) {
state = FINISH;
}
postInvalidateDelayed(200);
break;
}
}
invalidate();
break;
case FINISH:
//支付完成
mPath.reset();
if (secondProgress < 360) {
float sweepAngle = secondProgress;
canvas.drawArc(mRectF, -90f, sweepAngle, false, mPaint);
secondProgress += circleIncRate;
invalidate();
} else {
canvas.drawArc(mRectF, -90f, 360f, false, mPaint);
mPath.moveTo(firstLine.startPoint.x, firstLine.startPoint.y);
float lineX = x1 + lineProgress;
if (lineProgress < xDis2to1) {
//绘制第一条线
mPath.lineTo(lineX, firstLine.getY(lineX));
invalidate();
} else if (lineProgress >= xDis2to1 && lineProgress < xDis3to1) {
//绘制第二条线
mPath.lineTo(firstLine.endPoint.x, firstLine.endPoint.y);
mPath.lineTo(lineX, secondLine.getY(lineX));
invalidate();
} else {
//全部画完
mPath.lineTo(firstLine.endPoint.x, firstLine.endPoint.y);
mPath.lineTo(secondLine.endPoint.x, secondLine.endPoint.y);
}
lineProgress += increaseDis;
canvas.drawPath(mPath, mPaint);
}
break;
}
} private void reset() {
firstProgress = 0;
secondProgress = 0;
sweepLength = 0;
lineProgress = 0;
} enum State {
IDLE,
PROGRESS,
FINISH
} public void setState(State state) {
this.state = state;
invalidate();
} public void setOverPay(boolean endPay) {
isEndPay = endPay;
} class Line {
PointF startPoint;
PointF endPoint; float k;//比例系数
float b;//常数 Line(PointF startPoint, PointF endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint; k = (endPoint.y - startPoint.y) / (endPoint.x - startPoint.x);
b = startPoint.y - k * startPoint.x;
} float getY(float x) {
return k * x + b;
}
}
}

MainActivity.java

package com.loaderman.customviewdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button; public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private AlipayView alipay_view;
private Button btn_start_pay, btn_end_pay;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
alipay_view = (AlipayView) findViewById(R.id.alipay_view);
alipay_view.setState(AlipayView.State.IDLE);
btn_start_pay = (Button) findViewById(R.id.btn_start_pay);
btn_end_pay = (Button) findViewById(R.id.btn_end_pay);
btn_start_pay.setOnClickListener(this);
btn_end_pay.setOnClickListener(this);
} @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start_pay:
alipay_view.setOverPay(false);
alipay_view.setState(AlipayView.State.PROGRESS);
break;
case R.id.btn_end_pay:
alipay_view.setOverPay(true);
break;
}
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.loaderman.customviewdemo.MainActivity"> <com.loaderman.customviewdemo.AlipayView
android:id="@+id/alipay_view"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
<Button
android:id="@+id/btn_start_pay"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="开始支付"/> <Button
android:id="@+id/btn_end_pay"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="支付完成"/> </LinearLayout>

效果图:

自定义view防支付成功页面的更多相关文章

  1. 手把手带你做一个超炫酷loading成功动画view Android自定义view

    写在前面: 本篇可能是手把手自定义view系列最后一篇了,实际上我也是一周前才开始真正接触自定义view,通过这一周的练习,基本上已经熟练自定义view,能够应对一般的view需要,那么就以本篇来结尾 ...

  2. 到处都是坑的微信支付V3之 微信支付回调页面

    据上次 到处都是坑的微信支付V3 后很多园友在被虐了千百遍后终于跳转到了亲切的微信支付界面,但输入密码支付后却不知道怎么处理了,接下来补上支付后的处理流程. 1. html中根据前台支付后反馈信息成功 ...

  3. Thinkphp框架中自定义修改success和error页面

    Thinkphp框架中自定义修改success和error页面 Thinkphp框架的默认success和error太难看,可以自定义设置,步骤如下: (注意:TP原框架中的success跳转有问题, ...

  4. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(四):自定义T4模板快速生成页面

    前言:上篇介绍了下ko增删改查的封装,确实节省了大量的js代码.博主是一个喜欢偷懒的人,总觉得这些基础的增删改查效果能不能通过一个什么工具直接生成页面效果,啥代码都不用写了,那该多爽.于是研究了下T4 ...

  5. salesforce 零基础学习(五十)自定义View或者List以及查看系统原来的View或者List

    salesforce给我们提供了标准的页面,比如标准的页面包括标准的列表和标准的详细页视图.有的时候我们想要自定义视图,比如做一个项目的时候不希望使用者直接通过ID查看到标准的详细页,而是跳转到指定处 ...

  6. Dialog详解(包括进度条、PopupWindow、自定义view、自定义样式的对话框)

    Dialog详解(包括进度条.PopupWindow.自定义view.自定义样式的对话框)   Android中提供了多种对话框,在实际应用中我们可能会需要修改这些已有的对话框.本实例就是从实际出发, ...

  7. Android 自定义View合集

    自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...

  8. Android自定义View的实现方法,带你一步步深入了解View(四)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了,回 ...

  9. [原] Android 自定义View步骤

    例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能 ...

随机推荐

  1. linux shell 数组的使用

    引言 在Linux平台上工作,我们经常需要使用shell来编写一些有用.有意义的脚本程序.有时,会经常使用shell数组.那么,shell中的数组是怎么表现的呢,又是怎么定义的呢?接下来逐一的进行讲解 ...

  2. Delphi Label组件

  3. 使用sproxy.exe访问基于soap的webservice

    使用vc访问基于soap的webservice有多种方法,其中有一种是使用atlsoap,关于这个可以搜索sproxy.exe文章,不在这介绍(主要是我的写作能力太差).我写这个日记主要是项记录访问w ...

  4. 模拟赛小结:2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)

    比赛链接:传送门 两个半小时的时候横扫了铜.银区的所有题,签到成功混进金区.奈何后面没能开出新的题. 最后一个小时的时候xk灵机一动想出了D题的做法,讨论了一波感觉可行,赶紧去敲.结束前2分钟终于过了 ...

  5. oracle监听启动很慢

    TNS-12531: TNS:cannot allocate memory 首先查看内存,free -m 发现当前的空闲内存还有很多,那就不是内存不足的问题 想到之前重启过数据库服务器,查看主机名ho ...

  6. Linux环境测试机器端口连通性

    生产中,有很大一部分的问题都是由于不同机器间网络不同导致的,那么如何判断两台机器之间的连通性?本文介绍几种常见的方式: telnet方法wget方法ssh方法curl方法1. telnet方法格式:t ...

  7. noi.ac NA537 【Graph】

    本来以为过了...然后FST了... 吐槽:nmdGraph为什么不连通... 这题想法其实非常\(na\ddot{\imath}ve\),就是对于一个连通块先钦点一个点为根,颜色是\(1\),考虑到 ...

  8. Java的面向对象属性

    定义类 定义类的过程就是定义类的属性的过程: 类的属性就是累的静态属性的简称,指类内包含的各项数据. 类的服务被称为成员函数或方法. 继承extends 通过定义继承方法,子类可以获得父类的所有属性和 ...

  9. MongoDB学习笔记之文档

    #向集合中插入文档有两种方式(insert.save) db.col.insert({title: 'MongoDB 教程', description: 'MongoDB 是一个 Nosql 数据库' ...

  10. 【leetcode】1268. Search Suggestions System

    题目如下: Given an array of strings products and a string searchWord. We want to design a system that su ...