├── README.md
└── TestMockAlipay
├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── alipay
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── mock
│ │ └── alipay
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── mock
│ │ │ └── alipay
│ │ │ ├── Callback.java
│ │ │ ├── PasswordKeypad.java
│ │ │ └── view
│ │ │ ├── MDProgressBar.java
│ │ │ ├── PasswordKeyboard.java
│ │ │ └── PasswordView.java
│ └── res
│ │ ├── anim
│ │ ├── bottom_in.xml
│ │ └── bottom_out.xml
│ │ ├── drawable
│ │ ├── key_normal.9.png
│ │ ├── key_press.9.png
│ │ └── key_selector.xml
│ │ ├── layout
│ │ └── password_keypad.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── mock
│ └── alipay
│ └── ExampleUnitTest.java
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── testmockalipay
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── testmockalipay
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── testmockalipay
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/README.md:
--------------------------------------------------------------------------------
1 | # ImitateAlipayPasswordInput
2 | 参考支付宝密码输入对话框设计,已经封装成Android Library,导入即可使用。
3 | ##效果图
4 | 
5 | #欢迎大家在此基础上再次完善功能,提高用户体验。
6 |
--------------------------------------------------------------------------------
/TestMockAlipay/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/TestMockAlipay/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/TestMockAlipay/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/TestMockAlipay/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/TestMockAlipay/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/TestMockAlipay/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/TestMockAlipay/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/TestMockAlipay/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 23
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | compile fileTree(dir: 'libs', include: ['*.jar'])
26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
27 | exclude group: 'com.android.support', module: 'support-annotations'
28 | })
29 | compile 'com.android.support:appcompat-v7:23.4.0'
30 | testCompile 'junit:junit:4.12'
31 | }
32 |
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/chenjiawei/Downloads/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/src/androidTest/java/com/mock/alipay/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.mock.alipay;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.mock.alipay.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/src/main/java/com/mock/alipay/Callback.java:
--------------------------------------------------------------------------------
1 | package com.mock.alipay;
2 |
3 | /**
4 | * Created by chenjiawei on 16/8/26.
5 | */
6 | public interface Callback {
7 |
8 | void onForgetPassword();
9 |
10 | void onInputCompleted(CharSequence password);
11 |
12 | void onPasswordCorrectly();
13 |
14 | void onCancel();
15 | }
16 |
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/src/main/java/com/mock/alipay/PasswordKeypad.java:
--------------------------------------------------------------------------------
1 | package com.mock.alipay;
2 |
3 | import android.app.Activity;
4 | import android.content.DialogInterface;
5 | import android.graphics.drawable.ColorDrawable;
6 | import android.os.Bundle;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.app.DialogFragment;
9 | import android.text.TextUtils;
10 | import android.util.DisplayMetrics;
11 | import android.view.Gravity;
12 | import android.view.LayoutInflater;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.view.Window;
16 | import android.widget.RelativeLayout;
17 | import android.widget.TextView;
18 |
19 | import com.mock.alipay.view.MDProgressBar;
20 | import com.mock.alipay.view.PasswordKeyboard;
21 | import com.mock.alipay.view.PasswordView;
22 |
23 |
24 | /**
25 | * Created by chenjiawei on 16/8/30.
26 | * 防支付宝密码输入界面
27 | */
28 | public class PasswordKeypad extends DialogFragment implements View.OnClickListener, PasswordKeyboard.OnPasswordInputListener,
29 | MDProgressBar.OnPasswordCorrectlyListener {
30 |
31 | private TextView errorMsgTv;
32 |
33 | private Callback mCallback;
34 |
35 | private RelativeLayout passwordContainer;
36 |
37 | private MDProgressBar progressBar;
38 |
39 | private PasswordView passwordView;
40 |
41 | private int passwordCount;
42 |
43 | private boolean passwordState = true;
44 |
45 | PasswordKeyboard numberKeyBoard;
46 |
47 | private StringBuffer mPasswordBuffer = new StringBuffer();
48 |
49 | @Override
50 | public void onAttach(Activity context) {
51 | super.onAttach(context);
52 | if (context instanceof Callback) {
53 | mCallback = (Callback) context;
54 | }
55 | }
56 |
57 | @Nullable
58 | @Override
59 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
60 | return inflater.inflate(R.layout.password_keypad, container, false);
61 | }
62 |
63 | @Override
64 | public void onCreate(Bundle savedInstanceState) {
65 | super.onCreate(savedInstanceState);
66 | setStyle(DialogFragment.STYLE_NO_TITLE, 0);
67 | }
68 |
69 | @Override
70 | public void onStart() {
71 | super.onStart();
72 | DisplayMetrics dm = new DisplayMetrics();
73 | getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
74 | Window window = getDialog().getWindow();
75 | //去掉边框
76 | window.setBackgroundDrawable(new ColorDrawable(0xffffffff));
77 | window.setLayout(dm.widthPixels, window.getAttributes().height);
78 | window.setWindowAnimations(R.style.exist_menu_animstyle);
79 | window.setGravity(Gravity.BOTTOM);
80 | }
81 |
82 | @Override
83 | public void onViewCreated(View view, Bundle savedInstanceState) {
84 | super.onViewCreated(view, savedInstanceState);
85 | errorMsgTv = (TextView) view.findViewById(R.id.error_msg);
86 | TextView forgetPasswordTv = (TextView) view.findViewById(R.id.forget_password);
87 | TextView cancelTv = (TextView) view.findViewById(R.id.cancel_dialog);
88 |
89 | passwordContainer = (RelativeLayout) view.findViewById(R.id.password_content);
90 | progressBar = (MDProgressBar) view.findViewById(R.id.password_progressBar);
91 | progressBar.setOnPasswordCorrectlyListener(this);
92 | passwordView = (PasswordView) view.findViewById(R.id.password_inputBox);
93 | //设置密码长度
94 | if (passwordCount > 0) {
95 | passwordView.setPasswordCount(passwordCount);
96 | }
97 |
98 | numberKeyBoard = (PasswordKeyboard) view.findViewById(R.id.password_keyboard);
99 | numberKeyBoard.setOnPasswordInputListener(this);
100 |
101 | cancelTv.setOnClickListener(this);
102 | forgetPasswordTv.setOnClickListener(this);
103 | }
104 |
105 | /**
106 | * 设置密码长度
107 | */
108 | public void setPasswordCount(int passwordCount) {
109 | this.passwordCount = passwordCount;
110 | }
111 |
112 | @Override
113 | public void onClick(View v) {
114 | if (R.id.cancel_dialog == v.getId()) {
115 | if (mCallback != null) {
116 | mCallback.onCancel();
117 | }
118 | dismiss();
119 | } else if (R.id.forget_password == v.getId()) {
120 | if (mCallback != null) {
121 | mCallback.onForgetPassword();
122 | }
123 | }
124 | }
125 |
126 | public void setCallback(Callback callBack) {
127 | this.mCallback = callBack;
128 | }
129 |
130 | public void setPasswordState(boolean correct) {
131 | setPasswordState(correct, "");
132 | }
133 |
134 | public void setPasswordState(boolean correct, String msg) {
135 | passwordState = correct;
136 | if (correct) {
137 | progressBar.setSuccessfullyStatus();
138 | } else {
139 | numberKeyBoard.resetKeyboard();
140 | passwordView.clearPassword();
141 | progressBar.setVisibility(View.GONE);
142 | passwordContainer.setVisibility(View.VISIBLE);
143 | errorMsgTv.setText(msg);
144 | }
145 | }
146 |
147 | @Override
148 | public void onPasswordCorrectly() {
149 | if (mCallback != null) {
150 | mCallback.onPasswordCorrectly();
151 | }
152 | }
153 |
154 | private void startLoading(CharSequence password) {
155 | passwordContainer.setVisibility(View.INVISIBLE);
156 | progressBar.setVisibility(View.VISIBLE);
157 | if (mCallback != null) {
158 | mCallback.onInputCompleted(password);
159 | }
160 | }
161 |
162 | @Override
163 | public void onInput(String character) {
164 | if (PasswordKeyboard.DEL.equals(character)) {
165 | if (mPasswordBuffer.length() > 0) {
166 | mPasswordBuffer.delete(mPasswordBuffer.length() - 1, mPasswordBuffer.length());
167 | }
168 | } else if (PasswordKeyboard.DONE.equals(character)) {
169 | dismiss();
170 | } else {
171 | if (!passwordState) {
172 | if (!TextUtils.isEmpty(errorMsgTv.getText())) {
173 | errorMsgTv.setText("");
174 | }
175 | }
176 | mPasswordBuffer.append(character);
177 | }
178 | passwordView.setPassword(mPasswordBuffer);
179 | if (mPasswordBuffer.length() == passwordView.getPasswordCount()) {
180 | startLoading(mPasswordBuffer);
181 | }
182 | }
183 |
184 | @Override
185 | public void onDismiss(DialogInterface dialog) {
186 | super.onDismiss(dialog);
187 | if (mPasswordBuffer.length() > 0) {
188 | mPasswordBuffer.delete(0, mPasswordBuffer.length());
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/src/main/java/com/mock/alipay/view/MDProgressBar.java:
--------------------------------------------------------------------------------
1 | package com.mock.alipay.view;
2 |
3 | import android.animation.Animator;
4 | import android.animation.Animator.AnimatorListener;
5 | import android.animation.AnimatorSet;
6 | import android.animation.ValueAnimator;
7 | import android.animation.ValueAnimator.AnimatorUpdateListener;
8 | import android.content.Context;
9 | import android.content.res.TypedArray;
10 | import android.graphics.Canvas;
11 | import android.graphics.Color;
12 | import android.graphics.Paint;
13 | import android.graphics.RectF;
14 | import android.util.AttributeSet;
15 | import android.view.View;
16 | import android.view.animation.Animation;
17 | import android.view.animation.DecelerateInterpolator;
18 | import android.view.animation.LinearInterpolator;
19 | import android.view.animation.Transformation;
20 |
21 | import com.mock.alipay.R;
22 |
23 |
24 | /**
25 | * Created by chenjiawei on 16/10/28.
26 | */
27 | public class MDProgressBar extends View {
28 |
29 | private final static String TAG = MDProgressBar.class.getSimpleName();
30 |
31 | private static final float DEFAULT_MAX_ANGLE = -305f;
32 |
33 | private static final float DEFAULT_MIN_ANGLE = -19f;
34 |
35 | //默认的动画时间
36 | private static final int DEFAULT_DURATION = 660;
37 |
38 | private final static int DEFAULT_ARC_COLOR = Color.BLUE;
39 | //圆弧颜色
40 | private int arcColor = DEFAULT_ARC_COLOR;
41 |
42 | private AnimatorSet animatorSet;
43 |
44 | private float mBorderWidth;
45 |
46 | private Paint mPaint;
47 |
48 | private RectF arcRectF;
49 |
50 | private float startAngle = -45f;
51 |
52 | private float sweepAngle = -19f;
53 |
54 | private float incrementAngele = 0;
55 | //是否需要开始绘制对勾
56 | private boolean isNeedTick = false;
57 |
58 | private int mResize;
59 |
60 | private TickAnimation mTickAnimation;
61 | //判断"对勾"动画是否过半,"对勾"由两条线绘制而成。
62 | private boolean isAnimationOverHalf = false;
63 | //圆形进度条的半径
64 | private float mRadius;
65 |
66 | private float startY1;
67 |
68 | private float startX1;
69 |
70 | private float stopX1;
71 |
72 | private float stopY1;
73 |
74 | private float stopX2;
75 |
76 | private float stopY2;
77 |
78 | private OnPasswordCorrectlyListener mListener;
79 |
80 | public MDProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
81 | super(context, attrs, defStyleAttr);
82 | init(context, attrs);
83 | }
84 |
85 | public MDProgressBar(Context context, AttributeSet attrs) {
86 | super(context, attrs);
87 | init(context, attrs);
88 | }
89 |
90 | private void init(Context context, AttributeSet attrs) {
91 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.materialStatusProgressAttr);
92 | arcColor = typedArray.getColor(R.styleable.materialStatusProgressAttr_arcColor, Color.parseColor("#4a90e2"));
93 | mBorderWidth = typedArray.getDimension(R.styleable.materialStatusProgressAttr_progressBarBorderWidth,
94 | getResources().getDimension(R.dimen.material_status_progress_border));
95 | typedArray.recycle();
96 | mPaint = new Paint();
97 | mPaint.setColor(arcColor);
98 | mPaint.setStrokeWidth(mBorderWidth);
99 | mPaint.setAntiAlias(true);
100 | mPaint.setStyle(Paint.Style.STROKE);
101 | arcRectF = new RectF();
102 | mTickAnimation = new TickAnimation();
103 | mTickAnimation.setDuration(800);
104 | //对勾动画监听
105 | mTickAnimation.setAnimationListener(new Animation.AnimationListener() {
106 | @Override
107 | public void onAnimationStart(Animation animation) {
108 |
109 | }
110 |
111 | @Override
112 | public void onAnimationEnd(Animation animation) {
113 | //当对勾动画完成后,延迟一秒回掉,不然动画效果不明显
114 | if (mListener != null) {
115 | postDelayed(new Runnable() {
116 | @Override
117 | public void run() {
118 | mListener.onPasswordCorrectly();
119 |
120 | }
121 | }, 1000);
122 | }
123 | }
124 |
125 | @Override
126 | public void onAnimationRepeat(Animation animation) {
127 |
128 | }
129 | });
130 | }
131 |
132 | private void arcPaint() {
133 | mPaint.reset();
134 | mPaint.setColor(arcColor);
135 | mPaint.setStrokeWidth(mBorderWidth);
136 | mPaint.setAntiAlias(true);
137 | mPaint.setStyle(Paint.Style.STROKE);
138 | }
139 |
140 | private void linePaint() {
141 | mPaint.reset();
142 | mPaint.setColor(arcColor);
143 | mPaint.setStrokeWidth(mBorderWidth);
144 | mPaint.setAntiAlias(true);
145 | }
146 | //对勾动画完成回调
147 | public void setOnPasswordCorrectlyListener(OnPasswordCorrectlyListener listener) {
148 | this.mListener = listener;
149 | }
150 |
151 | @Override
152 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
153 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
154 | startY1 = getMeasuredHeight() / 2;
155 | mRadius = getMeasuredHeight() / 2 - 2 * mBorderWidth;
156 | startX1 = startY1 - getMeasuredHeight() / 5;
157 | }
158 |
159 | @Override
160 | protected void onDraw(Canvas canvas) {
161 | super.onDraw(canvas);
162 | arcPaint();
163 | canvas.drawArc(arcRectF, startAngle + incrementAngele, sweepAngle, false, mPaint);
164 | if (animatorSet == null || !animatorSet.isRunning() && !isNeedTick) {
165 | startAnimation();
166 | }
167 | if (isNeedTick) {
168 | //补全圆
169 | arcPaint();
170 | canvas.drawArc(arcRectF, startAngle + incrementAngele + sweepAngle, 360 - sweepAngle, false, mPaint);
171 | linePaint();
172 | //画第一根线
173 | canvas.drawLine(startX1, startY1, stopX1, stopY1, mPaint);
174 | if (isAnimationOverHalf) {
175 | //-2 +2 是为了两根线尽可能靠拢
176 | canvas.drawLine(stopX1 - 2, stopY1 + 2, stopX2, stopY2, mPaint);
177 | }
178 | }
179 | }
180 | //对勾动画
181 | private class TickAnimation extends Animation {
182 |
183 | @Override
184 | protected void applyTransformation(final float interpolatedTime, Transformation t) {
185 | super.applyTransformation(interpolatedTime, t);
186 | if (interpolatedTime <= 0.5f) {
187 | stopX1 = startX1 + mRadius / 3 * interpolatedTime * 2;
188 | stopY1 = startY1 + mRadius / 3 * interpolatedTime * 2;
189 | isAnimationOverHalf = false;
190 | } else {
191 | stopX2 = stopX1 + (mRadius - 20) * (interpolatedTime - 0.5f) * 2;
192 | stopY2 = stopY1 - (mRadius - 20) * (interpolatedTime - 0.5f) * 2;
193 | isAnimationOverHalf = true;
194 | }
195 | invalidate();
196 | }
197 | }
198 |
199 | @Override
200 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
201 | super.onSizeChanged(w, h, oldw, oldh);
202 | mResize = (w < h) ? w : h;
203 | setBound();
204 | }
205 |
206 | private void setBound() {
207 | int paddingLeft = getPaddingLeft();
208 | int paddingTop = getPaddingTop();
209 | arcRectF.set(paddingLeft + mBorderWidth, paddingTop + mBorderWidth, mResize - paddingLeft - mBorderWidth, mResize - paddingTop - mBorderWidth);
210 | }
211 |
212 | public void startAnimation() {
213 | isNeedTick = false;
214 | if (animatorSet != null && animatorSet.isRunning()) {
215 | animatorSet.cancel();
216 | }
217 | if (animatorSet == null) {
218 | animatorSet = new AnimatorSet();
219 | }
220 | AnimatorSet set = loopAnimator();
221 | animatorSet.play(set);
222 | animatorSet.addListener(new AnimatorListener() {
223 |
224 | private boolean isCancel = false;
225 |
226 | @Override
227 | public void onAnimationStart(Animator animation) {
228 |
229 | }
230 |
231 | @Override
232 | public void onAnimationRepeat(Animator animation) {
233 | }
234 |
235 | @Override
236 | public void onAnimationEnd(Animator animation) {
237 | if (!isCancel) {
238 | startAnimation();
239 | }
240 | }
241 |
242 | @Override
243 | public void onAnimationCancel(Animator animation) {
244 | isCancel = true;
245 | }
246 | });
247 | animatorSet.start();
248 | }
249 |
250 | /**
251 | * 进度条旋转的动画
252 | */
253 | private AnimatorSet loopAnimator() {
254 | //从小圈到大圈
255 | ValueAnimator holdAnimator1 = ValueAnimator.ofFloat(incrementAngele + DEFAULT_MIN_ANGLE, incrementAngele + 115f);
256 | holdAnimator1.addUpdateListener(new AnimatorUpdateListener() {
257 | @Override
258 | public void onAnimationUpdate(ValueAnimator animation) {
259 | incrementAngele = (float) animation.getAnimatedValue();
260 | }
261 | });
262 | holdAnimator1.setDuration(DEFAULT_DURATION);
263 | holdAnimator1.setInterpolator(new LinearInterpolator());
264 | ValueAnimator expandAnimator = ValueAnimator.ofFloat(DEFAULT_MIN_ANGLE, DEFAULT_MAX_ANGLE);
265 | expandAnimator.addUpdateListener(new AnimatorUpdateListener() {
266 | @Override
267 | public void onAnimationUpdate(ValueAnimator animation) {
268 | sweepAngle = (float) animation.getAnimatedValue();
269 | incrementAngele -= sweepAngle;
270 | invalidate();
271 | }
272 | });
273 | expandAnimator.setDuration(DEFAULT_DURATION);
274 | expandAnimator.setInterpolator(new DecelerateInterpolator(2));
275 | //从大圈到小圈
276 | ValueAnimator holdAnimator = ValueAnimator.ofFloat(startAngle, startAngle + 115f);
277 | holdAnimator.addUpdateListener(new AnimatorUpdateListener() {
278 | @Override
279 | public void onAnimationUpdate(ValueAnimator animation) {
280 | startAngle = (float) animation.getAnimatedValue();
281 | }
282 | });
283 |
284 | holdAnimator.setDuration(DEFAULT_DURATION);
285 | holdAnimator.setInterpolator(new LinearInterpolator());
286 | ValueAnimator narrowAnimator = ValueAnimator.ofFloat(DEFAULT_MAX_ANGLE, DEFAULT_MIN_ANGLE);
287 | narrowAnimator.addUpdateListener(new AnimatorUpdateListener() {
288 | @Override
289 | public void onAnimationUpdate(ValueAnimator animation) {
290 | sweepAngle = (float) animation.getAnimatedValue();
291 | invalidate();
292 | }
293 | });
294 |
295 | narrowAnimator.setDuration(DEFAULT_DURATION);
296 | narrowAnimator.setInterpolator(new DecelerateInterpolator(2));
297 |
298 | AnimatorSet set = new AnimatorSet();
299 | set.play(holdAnimator1).with(expandAnimator);
300 | set.play(holdAnimator).with(narrowAnimator).after(holdAnimator1);
301 | return set;
302 | }
303 | //清除动画
304 | private void cancelAnimator() {
305 | if (animatorSet != null) {
306 | animatorSet.cancel();
307 | isNeedTick = true;
308 | }
309 | }
310 |
311 | public void setSuccessfullyStatus() {
312 | if (animatorSet != null) {
313 | animatorSet.cancel();
314 | isNeedTick = true;
315 | startAnimation(mTickAnimation);
316 | }
317 | }
318 |
319 | @Override
320 | public void setVisibility(int visibility) {
321 | switch (visibility) {
322 | case View.VISIBLE:
323 | startAnimation();
324 | break;
325 | case View.INVISIBLE:
326 | cancelAnimator();
327 | break;
328 | case View.GONE:
329 | cancelAnimator();
330 | break;
331 | default:
332 | break;
333 | }
334 | super.setVisibility(visibility);
335 | }
336 |
337 | public void setBorderWidth(int width) {
338 | this.mBorderWidth = width;
339 | }
340 |
341 | public void setArcColor(int color) {
342 | this.arcColor = color;
343 | }
344 |
345 | public interface OnPasswordCorrectlyListener {
346 | void onPasswordCorrectly();
347 | }
348 |
349 | }
--------------------------------------------------------------------------------
/TestMockAlipay/alipay/src/main/java/com/mock/alipay/view/PasswordKeyboard.java:
--------------------------------------------------------------------------------
1 | package com.mock.alipay.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.os.Handler;
8 | import android.os.Message;
9 | import android.util.AttributeSet;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.view.WindowManager;
14 | import android.widget.Button;
15 | import android.widget.GridLayout;
16 |
17 |
18 | import com.mock.alipay.R;
19 |
20 | import java.util.ArrayList;
21 | import java.util.List;
22 | import java.util.Random;
23 |
24 | /**
25 | * Created by chenjiawei on 16/10/28.
26 | */
27 |
28 | public class PasswordKeyboard extends GridLayout implements View.OnClickListener, View.OnTouchListener {
29 |
30 | public static final String DEL = "删除";
31 |
32 | public static final String DONE = "OK";
33 | //因为UED是给的是iPhone设计稿,所以是按照等比的思想设置键盘Key的高度和宽度
34 | private static final int IPHONE = 779;
35 | //每个键盘Key的宽度,为屏幕宽度的三分之一
36 | private int keyWidth = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth() / 3;
37 | //每个键盘Key的高度
38 | private int keyHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight() * 59 / IPHONE;
39 |
40 | private int screenWidth = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();
41 |
42 | private Paint mPaint;
43 | //List集合存储Key,方便每次输错都能再次随机数字键盘
44 | private final List