From 3d85b63282eeabaf809e908b688196bba7d4b3cc Mon Sep 17 00:00:00 2001 From: xiaoyao Date: Thu, 13 Jul 2017 15:44:37 +0800 Subject: [PATCH 01/43] =?UTF-8?q?=E5=AE=8C=E5=96=84LoadingButton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widget/LoadingButton.java | 171 +++++++----------- 1 file changed, 66 insertions(+), 105 deletions(-) diff --git a/app/src/main/java/com/allen/androidcustomview/widget/LoadingButton.java b/app/src/main/java/com/allen/androidcustomview/widget/LoadingButton.java index 4a7e994..0b56955 100644 --- a/app/src/main/java/com/allen/androidcustomview/widget/LoadingButton.java +++ b/app/src/main/java/com/allen/androidcustomview/widget/LoadingButton.java @@ -1,8 +1,6 @@ package com.allen.androidcustomview.widget; -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; + import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -28,27 +26,27 @@ public class LoadingButton extends Button { private Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint circlePaint1 = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint circlePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint circlePaint3 = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + + private static final int POINT_COLOR_1 = 0x4CFFFFFF; + private static final int POINT_COLOR_2 = 0x7FFFFFFF; + private static final int POINT_COLOR_3 = 0xFFFFFFFF; - private int circleColor = 0xffffff; - // private int circleColor = 0xf66b12; - private int duration = 1000; + private int duration = 300; private float centerX; private float centerY; private float radius; - private AnimatorSet animatorSet; - private ValueAnimator animator1; - private ValueAnimator animator2; - private ValueAnimator animator3; - private boolean isLoading = false; + private int mLoadingIndex = 0; + private Runnable mRunnable; + + public LoadingButton(Context context) { this(context, null); } @@ -58,84 +56,18 @@ public LoadingButton(Context context, @Nullable AttributeSet attrs) { initPaint(); - animatorSet = new AnimatorSet(); - circleAnimator1(); - circleAnimator2(); - circleAnimator3(); - - animatorSet.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - - } - - @Override - public void onAnimationEnd(Animator animation) { - animatorSet.start(); - } - - @Override - public void onAnimationCancel(Animator animation) { - - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - }); - animatorSet.play(animator1).with(animator2).with(animator3); - - } - - private void circleAnimator1() { - animator1 = ValueAnimator.ofInt(255, 76, 165); - animator1.setDuration(duration); - animator1.setRepeatCount(ValueAnimator.INFINITE); - animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - circlePaint1.setAlpha((Integer) animation.getAnimatedValue()); - invalidate(); - } - }); } - private void circleAnimator2() { - animator2 = ValueAnimator.ofInt(165, 255, 76); - animator2.setDuration(duration); - animator2.setRepeatCount(ValueAnimator.INFINITE); - animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - circlePaint2.setAlpha((Integer) animation.getAnimatedValue()); - invalidate(); - } - }); - } - private void circleAnimator3() { - animator3 = ValueAnimator.ofInt(76, 165, 255); - animator3.setDuration(duration); - animator3.setRepeatCount(ValueAnimator.INFINITE); - animator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + private void initPaint() { + initTextPaint(); + circlePaint = getPaint(dp2px(1), Paint.Style.FILL); + mRunnable = new Runnable() { @Override - public void onAnimationUpdate(ValueAnimator animation) { - circlePaint3.setAlpha((Integer) animation.getAnimatedValue()); + public void run() { invalidate(); } - }); - } - - private void initPaint() { - initTextPaint(); - - circlePaint1 = getPaint(dp2px(1), circleColor, Paint.Style.FILL); - circlePaint1.setAlpha(255); - circlePaint2 = getPaint(dp2px(1), circleColor, Paint.Style.FILL); - circlePaint2.setAlpha(165); - circlePaint3 = getPaint(dp2px(1), circleColor, Paint.Style.FILL); - circlePaint3.setAlpha(76); + }; } /** @@ -163,7 +95,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { centerY = mHeight / 2; radius = mHeight / 8; - Log.d("allen", "onMeasure: "+centerX); + Log.d("allen", "onMeasure: " + centerX); } @@ -171,21 +103,49 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onDraw(Canvas canvas) { if (isLoading) { - drawLoading(canvas); + drawLoading(canvas, mLoadingIndex); + mLoadingIndex = (mLoadingIndex + 1) % 3; + postDelayed(mRunnable, duration); + } else { super.onDraw(canvas); } } - private void drawLoading(Canvas canvas) { - canvas.drawCircle(centerX - radius * 4, centerY, radius, circlePaint1); - - canvas.drawCircle(centerX, centerY, radius, circlePaint2); + private void drawLoading(Canvas canvas, int index) { + if (index < 0 || index > 2) + return; + switch (index) { + case 0: + circlePaint.setColor(POINT_COLOR_1); + canvas.drawCircle(centerX - radius * 4, centerY, radius, circlePaint); + circlePaint.setColor(POINT_COLOR_2); + canvas.drawCircle(centerX, centerY, radius, circlePaint); + circlePaint.setColor(POINT_COLOR_3); + canvas.drawCircle(centerX + radius * 4, centerY, radius, circlePaint); + break; + case 1: + circlePaint.setColor(POINT_COLOR_3); + canvas.drawCircle(centerX - radius * 4, centerY, radius, circlePaint); + circlePaint.setColor(POINT_COLOR_1); + canvas.drawCircle(centerX, centerY, radius, circlePaint); + circlePaint.setColor(POINT_COLOR_2); + canvas.drawCircle(centerX + radius * 4, centerY, radius, circlePaint); + break; + case 2: + circlePaint.setColor(POINT_COLOR_2); + canvas.drawCircle(centerX - radius * 4, centerY, radius, circlePaint); + circlePaint.setColor(POINT_COLOR_3); + canvas.drawCircle(centerX, centerY, radius, circlePaint); + circlePaint.setColor(POINT_COLOR_1); + canvas.drawCircle(centerX + radius * 4, centerY, radius, circlePaint); + break; + } - canvas.drawCircle(centerX + radius * 4, centerY, radius, circlePaint3); } + /** * 绘制文字 * @@ -201,32 +161,33 @@ private void drawText(Canvas canvas, String textString) { public void startLoading() { - if (animatorSet != null) { - isLoading = true; - animatorSet.start(); - } + if (isLoading) + return; + isLoading = true; + mLoadingIndex = 0; + invalidate(); } public void stopLoading() { - if (animatorSet != null) { - isLoading = false; - animatorSet.end(); - postInvalidate(); - } + if (!isLoading) + return; + isLoading = false; + } + + public boolean isLoading() { + return isLoading; } /** * 统一处理paint * * @param strokeWidth - * @param color * @param style * @return */ - public Paint getPaint(int strokeWidth, int color, Paint.Style style) { + public Paint getPaint(int strokeWidth, Paint.Style style) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStrokeWidth(strokeWidth); - paint.setColor(color); paint.setAntiAlias(true); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStyle(style); From 269455ddd300bfd0f0f584e03d5ace1c5e7314ee Mon Sep 17 00:00:00 2001 From: zzq Date: Mon, 7 Aug 2017 17:54:11 +0800 Subject: [PATCH 02/43] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=AF=86=E7=A0=81=E8=BE=93=E5=85=A5=E6=A1=86=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E7=89=88=E5=A2=9E=E5=8A=A0=E6=AF=8F=E4=B8=AA=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E9=80=89=E4=B8=AD=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 3 +- .../widget/PayPsdInputView.java | 34 +++++++++++++------ .../main/res/layout/activity_pay_psd_view.xml | 2 +- app/src/main/res/values/attrs.xml | 1 + 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c027c99..7d1d990 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,7 +24,8 @@ - + diff --git a/app/src/main/java/com/allen/androidcustomview/widget/PayPsdInputView.java b/app/src/main/java/com/allen/androidcustomview/widget/PayPsdInputView.java index 1e4ceff..487e040 100644 --- a/app/src/main/java/com/allen/androidcustomview/widget/PayPsdInputView.java +++ b/app/src/main/java/com/allen/androidcustomview/widget/PayPsdInputView.java @@ -13,7 +13,6 @@ import com.allen.androidcustomview.R; - import static android.graphics.Paint.ANTI_ALIAS_FLAG; /** @@ -83,8 +82,9 @@ public class PayPsdInputView extends EditText { * 竖直分割线的颜色 */ private int divideLineColor = Color.GRAY; + private int focusedColor = Color.BLUE; private RectF rectF = new RectF(); - + private RectF focusedRecF = new RectF(); private int psdType = 0; private final static int psdType_weChat = 0; private final static int psdType_bottomLine = 1; @@ -111,6 +111,11 @@ public class PayPsdInputView extends EditText { */ private String mComparePassword = null; + /** + * 当前输入的位置索引 + */ + private int position = 0; + private onPasswordListener mListener; public PayPsdInputView(Context context, AttributeSet attrs) { @@ -137,6 +142,7 @@ private void getAtt(AttributeSet attrs) { divideLineColor = typedArray.getColor(R.styleable.PayPsdInputView_divideLineColor, divideLineColor); psdType = typedArray.getInt(R.styleable.PayPsdInputView_psdType, psdType); rectAngle = typedArray.getDimensionPixelOffset(R.styleable.PayPsdInputView_rectAngle, rectAngle); + focusedColor = typedArray.getColor(R.styleable.PayPsdInputView_focusedColor, focusedColor); typedArray.recycle(); } @@ -174,7 +180,6 @@ private Paint getPaint(int strokeWidth, Paint.Style style, int color) { return paint; } - @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); @@ -195,11 +200,12 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { @Override protected void onDraw(Canvas canvas) { //不删除的画会默认绘制输入的文字 -// super.onDraw(canvas); +// super.onDraw(canvas); switch (psdType) { case psdType_weChat: drawWeChatBorder(canvas); + drawItemFocused(canvas, position); break; case psdType_bottomLine: drawBottomBorder(canvas); @@ -209,7 +215,6 @@ protected void onDraw(Canvas canvas) { drawPsdCircle(canvas); } - /** * 画微信支付密码的样式 * @@ -229,6 +234,14 @@ private void drawWeChatBorder(Canvas canvas) { } + private void drawItemFocused(Canvas canvas, int position) { + if (position > maxCount - 1) { + return; + } + focusedRecF.set(position * divideLineWStartX, 0, (position + 1) * divideLineWStartX, + height); + canvas.drawRoundRect(focusedRecF, rectAngle, rectAngle, getPaint(3, Paint.Style.STROKE, focusedColor)); + } /** * 画底部显示的分割线 @@ -263,6 +276,7 @@ private void drawPsdCircle(Canvas canvas) { @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); + this.position = start + lengthAfter; textLength = text.toString().length(); if (mComparePassword != null && textLength == maxCount) { @@ -295,6 +309,11 @@ public String getPasswordString() { return getText().toString().trim(); } + public void setComparePassword(String comparePassword, onPasswordListener listener) { + mComparePassword = comparePassword; + mListener = listener; + } + /** * 密码比较监听 */ @@ -303,9 +322,4 @@ public interface onPasswordListener { void onEqual(String psd); } - - public void setComparePassword(String comparePassword, onPasswordListener listener) { - mComparePassword = comparePassword; - mListener = listener; - } } diff --git a/app/src/main/res/layout/activity_pay_psd_view.xml b/app/src/main/res/layout/activity_pay_psd_view.xml index cc89a0f..01a8df9 100644 --- a/app/src/main/res/layout/activity_pay_psd_view.xml +++ b/app/src/main/res/layout/activity_pay_psd_view.xml @@ -16,7 +16,7 @@ android:inputType="number" psd:maxCount="6" psd:psdType="weChat" - psd:rectAngle="0dp" /> + psd:rectAngle="4dp" /> + From d1a18735bb434d95b446fde02278912a631dfbf3 Mon Sep 17 00:00:00 2001 From: xiaoyao Date: Fri, 15 Sep 2017 17:58:11 +0800 Subject: [PATCH 03/43] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=B0=8F=E7=90=83?= =?UTF-8?q?=E6=8B=96=E6=8B=BD=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 11 +- .../activity/DragBallActivity.java | 57 +++ .../activity/FingerprintActivity.java | 106 +++++ .../activity/MainActivity.java | 19 +- .../utils/FingerprintUtil.java | 102 +++++ .../widget/DragBallView.java | 404 ++++++++++++++++++ .../main/res/layout/activity_drag_ball.xml | 48 +++ .../main/res/layout/activity_finger_print.xml | 23 + app/src/main/res/layout/activity_main.xml | 12 + 9 files changed, 776 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/allen/androidcustomview/activity/DragBallActivity.java create mode 100644 app/src/main/java/com/allen/androidcustomview/activity/FingerprintActivity.java create mode 100644 app/src/main/java/com/allen/androidcustomview/utils/FingerprintUtil.java create mode 100644 app/src/main/java/com/allen/androidcustomview/widget/DragBallView.java create mode 100644 app/src/main/res/layout/activity_drag_ball.xml create mode 100644 app/src/main/res/layout/activity_finger_print.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7d1d990..de88352 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ package="com.allen.androidcustomview"> + + - - + + + + \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/activity/DragBallActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/DragBallActivity.java new file mode 100644 index 0000000..390aff8 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/activity/DragBallActivity.java @@ -0,0 +1,57 @@ +package com.allen.androidcustomview.activity; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.allen.androidcustomview.R; +import com.allen.androidcustomview.widget.DragBallView; + + +public class DragBallActivity extends AppCompatActivity { + + + private Button resetBtn, msgCountBtn; + private DragBallView dragBallView; + private EditText msgCountEt; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_drag_ball); + resetBtn = (Button) findViewById(R.id.reset_btn); + msgCountBtn = (Button) findViewById(R.id.msg_count_btn); + dragBallView = (DragBallView) findViewById(R.id.drag_ball_view); + + msgCountEt = (EditText) findViewById(R.id.msg_count_et); + + resetBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dragBallView.reset(); + } + }); + + msgCountBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!TextUtils.isEmpty(msgCountEt.getText().toString().trim())) { + int count = Integer.valueOf(msgCountEt.getText().toString().trim()); + dragBallView.setMsgCount(count); + } + } + }); + + dragBallView.setOnDragBallListener(new DragBallView.OnDragBallListener() { + @Override + public void onDisappear() { + Toast.makeText(DragBallActivity.this, "消失了", Toast.LENGTH_SHORT).show(); + } + }); + } +} diff --git a/app/src/main/java/com/allen/androidcustomview/activity/FingerprintActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/FingerprintActivity.java new file mode 100644 index 0000000..45ca65e --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/activity/FingerprintActivity.java @@ -0,0 +1,106 @@ +package com.allen.androidcustomview.activity; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.hardware.fingerprint.FingerprintManagerCompat; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.allen.androidcustomview.R; +import com.allen.androidcustomview.utils.FingerprintUtil; + +/** + * Created by xiaoyao on 2017/9/14. + */ + +public class FingerprintActivity extends AppCompatActivity { + + private static final String TAG = "allen"; + + + TextView resultTv; + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_finger_print); + + resultTv = (TextView) findViewById(R.id.result_tv); + findViewById(R.id.open_finger_btn).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + openFinger(); + } + }); + + + findViewById(R.id.cancel_finger_btn).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FingerprintUtil.cancel(); + } + }); + + + } + + private void openFinger() { + + FingerprintUtil.callFingerPrint(this, new FingerprintUtil.OnCallBackListener() { + @Override + public void onSupportFailed() { + Toast.makeText(FingerprintActivity.this, "当前设备不支持指纹", Toast.LENGTH_LONG).show(); + Log.e(TAG, "当前设备不支持指纹"); + } + + @Override + public void onInsecurity() { + Toast.makeText(FingerprintActivity.this, "当前设备未处于安全保护中", Toast.LENGTH_LONG).show(); + Log.e(TAG, "当前设备未处于安全保护中"); + } + + @Override + public void onEnrollFailed() { + Toast.makeText(FingerprintActivity.this, "请到设置中设置指纹", Toast.LENGTH_LONG).show(); + Log.e(TAG, "请到设置中设置指纹"); + } + + @Override + public void onAuthenticationStart() { + Toast.makeText(FingerprintActivity.this, "验证开始", Toast.LENGTH_LONG).show(); + + Log.e(TAG, "onAuthenticationStart: "); + } + + @Override + public void onAuthenticationError(int errMsgId, CharSequence errString) { + Toast.makeText(FingerprintActivity.this, errString, Toast.LENGTH_LONG).show(); + Log.e(TAG, "onAuthenticationError: "); + } + + @Override + public void onAuthenticationFailed() { + Toast.makeText(FingerprintActivity.this, "解锁失败", Toast.LENGTH_LONG).show(); + + Log.e(TAG, "解锁失败"); + } + + @Override + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + Toast.makeText(FingerprintActivity.this, helpString, Toast.LENGTH_LONG).show(); + + Log.e(TAG, "onAuthenticationHelp: "); + } + + @Override + public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) { + Toast.makeText(FingerprintActivity.this, "解锁成功", Toast.LENGTH_LONG).show(); + Log.e(TAG, "解锁成功"); + resultTv.setText(result.getClass().getName()); + } + }); + } +} diff --git a/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.java index f7ffd1a..e781ed4 100644 --- a/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.java +++ b/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.java @@ -5,12 +5,9 @@ import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; -import android.widget.TextView; -import android.widget.Toast; import com.allen.androidcustomview.R; import com.allen.androidcustomview.tagview.TagActivity; -import com.allen.androidcustomview.widget.FadeInTextView; public class MainActivity extends AppCompatActivity { @@ -25,6 +22,8 @@ public class MainActivity extends AppCompatActivity { private Button progress_btn; private Button animationViewBtn; + private Button huaweiViewBtn; + private Button fingerBtn; @Override @@ -41,6 +40,8 @@ protected void onCreate(Bundle savedInstanceState) { pay_psd_view_btn = (Button) findViewById(R.id.pay_psd_view_btn); progress_btn = (Button) findViewById(R.id.progress_btn); animationViewBtn = (Button) findViewById(R.id.animation_view_btn); + huaweiViewBtn = (Button) findViewById(R.id.huawei_view_btn); + fingerBtn = (Button) findViewById(R.id.finger_btn); button_bubble.setOnClickListener(new View.OnClickListener() { @Override @@ -97,6 +98,18 @@ public void onClick(View v) { startActivity(new Intent(MainActivity.this, AnimationViewActivity.class)); } }); + huaweiViewBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(new Intent(MainActivity.this, DragBallActivity.class)); + } + }); + fingerBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(new Intent(MainActivity.this, FingerprintActivity.class)); + } + }); } diff --git a/app/src/main/java/com/allen/androidcustomview/utils/FingerprintUtil.java b/app/src/main/java/com/allen/androidcustomview/utils/FingerprintUtil.java new file mode 100644 index 0000000..6f016c3 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/utils/FingerprintUtil.java @@ -0,0 +1,102 @@ +package com.allen.androidcustomview.utils; + +import android.app.KeyguardManager; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.hardware.fingerprint.FingerprintManagerCompat; +import android.support.v4.os.CancellationSignal; + +/** + * Created by xiaoyao on 2017/9/14. + * 指纹识别工具类 + */ + +public class FingerprintUtil { + + private static CancellationSignal cancellationSignal; + + public static void callFingerPrint(Context context, final OnCallBackListener listener) { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return; + } + FingerprintManagerCompat managerCompat = FingerprintManagerCompat.from(context); + if (!managerCompat.isHardwareDetected()) { //判断设备是否支持 + if (listener != null) + listener.onSupportFailed(); + return; + } + + KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); + if (!keyguardManager.isKeyguardSecure()) {//判断设备是否处于安全保护中 + if (listener != null) + listener.onInsecurity(); + return; + } + + if (!managerCompat.hasEnrolledFingerprints()) { //判断设备是否已经注册过指纹 + if (listener != null) + listener.onEnrollFailed(); //未注册 + return; + } + + if (listener != null) + listener.onAuthenticationStart(); //开始指纹识别 + + cancellationSignal = new CancellationSignal(); //必须重新实例化,否则cancel 过一次就不能再使用了 + + managerCompat.authenticate(null, 0, cancellationSignal, new FingerprintManagerCompat.AuthenticationCallback() { + // 当出现错误的时候回调此函数,比如多次尝试都失败了的时候,errString是错误信息,比如华为的提示就是:尝试次数过多,请稍后再试。 + @Override + public void onAuthenticationError(int errMsgId, CharSequence errString) { + if (listener != null) + listener.onAuthenticationError(errMsgId, errString); + } + + // 当指纹验证失败的时候会回调此函数,失败之后允许多次尝试,失败次数过多会停止响应一段时间然后再停止sensor的工作 + @Override + public void onAuthenticationFailed() { + if (listener != null) + listener.onAuthenticationFailed(); + } + + @Override + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + if (listener != null) + listener.onAuthenticationHelp(helpMsgId, helpString); + } + + // 当验证的指纹成功时会回调此函数,然后不再监听指纹sensor + @Override + public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) { + if (listener != null) + listener.onAuthenticationSucceeded(result); + } + }, null); + + } + + public interface OnCallBackListener { + void onSupportFailed(); + + void onInsecurity(); + + void onEnrollFailed(); + + void onAuthenticationStart(); + + void onAuthenticationError(int errMsgId, CharSequence errString); + + void onAuthenticationFailed(); + + void onAuthenticationHelp(int helpMsgId, CharSequence helpString); + + void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result); + } + + public static void cancel() { + if (cancellationSignal != null) + cancellationSignal.cancel(); + } +} diff --git a/app/src/main/java/com/allen/androidcustomview/widget/DragBallView.java b/app/src/main/java/com/allen/androidcustomview/widget/DragBallView.java new file mode 100644 index 0000000..f767e5f --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/widget/DragBallView.java @@ -0,0 +1,404 @@ +package com.allen.androidcustomview.widget; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PointF; +import android.graphics.Rect; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.BounceInterpolator; + +/** + * Created by xiaoyao on 2017/9/7. + * 仿QQ消息红点拖拽效果view + */ + +public class DragBallView extends View { + + private Paint circlePaint; + private Paint textPaint; + + private int circleColor = Color.RED; + private float radiusStart; + private float radiusEnd; + + private Path path; + + private int startX; + private int startY; + + private boolean mIsCanDrag = false; + private boolean isOutOfRang = false; + private boolean disappear = false; + + private float maxDistance; + + //贝塞尔曲线需要的点 + private PointF pointA; + private PointF pointB; + private PointF pointC; + private PointF pointD; + //控制点坐标 + private PointF pointO; + + //起始位置点 + private PointF pointStart; + //拖拽位置点 + private PointF pointEnd; + + //根据滑动位置动态改变圆的半径 + private float currentRadiusStart; + private float currentRadiusEnd; + private Rect textRect = new Rect(); + + //消息数 + private int msgCount = 0; + + private OnDragBallListener onDragBallListener; + + public DragBallView(Context context) { + this(context, null); + } + + public DragBallView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public DragBallView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initPaint(); + initPoint(); + + } + + /** + * 初始化所有点 + */ + private void initPoint() { + pointStart = new PointF(startX, startY); + + pointEnd = new PointF(startX, startY); + + pointA = new PointF(); + pointB = new PointF(); + pointC = new PointF(); + pointD = new PointF(); + + pointO = new PointF(); + + } + + /** + * 初始化画笔 + */ + private void initPaint() { + + circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + circlePaint.setColor(circleColor); + circlePaint.setAntiAlias(true); + circlePaint.setStyle(Paint.Style.FILL_AND_STROKE); + + path = new Path(); + initTextPaint(); + } + + /** + * 初始化文字画笔 + */ + private void initTextPaint() { + textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + textPaint.setTextSize(sp2px(13)); + textPaint.setColor(Color.WHITE); + textPaint.setTextAlign(Paint.Align.CENTER); + textPaint.setAntiAlias(true); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + startX = w / 2; + startY = h / 2; + maxDistance = dp2px(100); + radiusStart = dp2px(15); + radiusEnd = dp2px(15); + + currentRadiusEnd = radiusEnd; + currentRadiusStart = radiusStart; + pointEnd.set(startX, startY); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + pointStart.set(startX, startY); + if (isOutOfRang) { + if (!disappear) { + drawEndBall(canvas); + } + } else { + drawStartBall(canvas, pointStart, currentRadiusStart); + drawEndBall(canvas); + drawBezier(canvas); + } + + if (!disappear) { + if (msgCount > 0) { + drawText(canvas, msgCount, pointEnd); + } + } + } + + /** + * 绘制文字 + * + * @param canvas 画布 + */ + private void drawText(Canvas canvas, int msgCount, PointF point) { + textRect.left = (int) (point.x - radiusStart); + textRect.top = (int) (point.y - radiusStart); + textRect.right = (int) (point.x + radiusStart); + textRect.bottom = (int) (point.y + radiusStart); + Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt(); + int baseline = (textRect.bottom + textRect.top - fontMetrics.bottom - fontMetrics.top) / 2; + //文字绘制到整个布局的中心位置 + canvas.drawText(msgCount > 99 ? "99+" : msgCount + "", textRect.centerX(), baseline, textPaint); + } + + /** + * 画起始小球 + * + * @param canvas 画布 + * @param pointF 点坐标 + * @param radius 半径 + */ + private void drawStartBall(Canvas canvas, PointF pointF, float radius) { + canvas.drawCircle(pointF.x, pointF.y, radius, circlePaint); + + } + + /** + * 画拖拽结束的小球 + * + * @param canvas 画布 + */ + private void drawEndBall(Canvas canvas) { + canvas.drawCircle(pointEnd.x, pointEnd.y, currentRadiusEnd, circlePaint); + } + + /** + * 画贝塞尔曲线 + * + * @param canvas 画布 + */ + private void drawBezier(Canvas canvas) { + path.reset(); + path.moveTo(pointA.x, pointA.y); + path.quadTo(pointO.x, pointO.y, pointB.x, pointB.y); + path.lineTo(pointC.x, pointC.y); + path.quadTo(pointO.x, pointO.y, pointD.x, pointD.y); + path.lineTo(pointA.x, pointA.y); + path.close(); + + canvas.drawPath(path, circlePaint); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + + float currentX; + float currentY; + + switch (event.getAction()) { + + case MotionEvent.ACTION_DOWN: + setIsCanDrag(event); + break; + case MotionEvent.ACTION_MOVE: + + if (mIsCanDrag) { + + currentX = event.getX(); + currentY = event.getY(); + //设置拖拽圆的坐标 + pointEnd.set(currentX, currentY); + + if (!isOutOfRang) { + setCurrentRadius(); + setABCDOPoint(); + } + + invalidate(); + } + + break; + case MotionEvent.ACTION_UP: + if (mIsCanDrag) { + if (isOutOfRang) { + //消失动画 + disappear = true; + if (onDragBallListener != null) { + onDragBallListener.onDisappear(); + } + invalidate(); + } else { + disappear = false; + //回弹动画 + final float a = (pointEnd.y - pointStart.y) / (pointEnd.x - pointStart.x); + ValueAnimator valueAnimator = ValueAnimator.ofFloat(pointEnd.x, pointStart.x); + valueAnimator.setDuration(500); + valueAnimator.setInterpolator(new BounceInterpolator()); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float x = (float) animation.getAnimatedValue(); + + float y = pointStart.y + a * (x - pointStart.x); + + pointEnd.set(x, y); + setCurrentRadius(); + + setABCDOPoint(); + + invalidate(); + + } + }); + valueAnimator.start(); + } + } + break; + } + return true; + } + + /** + * 设置当前计算的到的半径 + */ + private void setCurrentRadius() { + //两个圆心之间的距离 + float distance = (float) Math.sqrt(Math.pow(pointStart.x - pointEnd.x, 2) + Math.pow(pointStart.y - pointEnd.y, 2)); + + //拖拽距离在设置的最大值范围内才绘制贝塞尔图形 + if (distance <= maxDistance) { + //比例系数 控制两圆半径缩放 + float percent = distance / maxDistance; + + currentRadiusStart = (1 - percent * 0.6f) * radiusStart; + currentRadiusEnd = (1 + percent * 0.2f) * radiusEnd; + + isOutOfRang = false; + } else { + isOutOfRang = true; + currentRadiusStart = radiusStart; + currentRadiusEnd = radiusEnd; + } + } + + /** + * 判断是否可以拖拽 + * + * @param event event + */ + private void setIsCanDrag(MotionEvent event) { + Rect rect = new Rect(); + rect.left = (int) (startX - radiusStart); + rect.top = (int) (startY - radiusStart); + rect.right = (int) (startX + radiusStart); + rect.bottom = (int) (startY + radiusStart); + + //触摸点是否在圆的坐标域内 + mIsCanDrag = rect.contains((int) event.getX(), (int) event.getY()); + } + + /** + * 设置贝塞尔曲线的相关点坐标 计算方式参照结算图即可看明白 + * (ps为了画个清楚这个图花了不少功夫哦) + */ + private void setABCDOPoint() { + //控制点坐标 + pointO.set((pointStart.x + pointEnd.x) / 2.0f, (pointStart.y + pointEnd.y) / 2.0f); + + float x = pointEnd.x - pointStart.x; + float y = pointEnd.y - pointStart.y; + + //斜率 tanA=rate + double rate; + rate = x / y; + //角度 根据反正切函数算角度 + float angle = (float) Math.atan(rate); + + pointA.x = (float) (pointStart.x + Math.cos(angle) * currentRadiusStart); + pointA.y = (float) (pointStart.y - Math.sin(angle) * currentRadiusStart); + + pointB.x = (float) (pointEnd.x + Math.cos(angle) * currentRadiusEnd); + pointB.y = (float) (pointEnd.y - Math.sin(angle) * currentRadiusEnd); + + pointC.x = (float) (pointEnd.x - Math.cos(angle) * currentRadiusEnd); + pointC.y = (float) (pointEnd.y + Math.sin(angle) * currentRadiusEnd); + + pointD.x = (float) (pointStart.x - Math.cos(angle) * currentRadiusStart); + pointD.y = (float) (pointStart.y + Math.sin(angle) * currentRadiusStart); + } + + /** + * 设置消息数 + * + * @param count 消息个数 + */ + public void setMsgCount(int count) { + msgCount = count; + invalidate(); + } + + public void reset() { + msgCount = 0; + mIsCanDrag = false; + isOutOfRang = false; + disappear = false; + pointStart.set(startX, startY); + pointEnd.set(startX, startY); + + setABCDOPoint(); + invalidate(); + } + + public void setOnDragBallListener(OnDragBallListener onDragBallListener) { + this.onDragBallListener = onDragBallListener; + } + + /** + * 回调事件 + */ + public interface OnDragBallListener { + void onDisappear(); + } + + /** + * dp 2 px + * + * @param dpVal + */ + protected int dp2px(int dpVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + dpVal, getResources().getDisplayMetrics()); + } + + /** + * sp 2 px + * + * @param spVal + * @return + */ + protected int sp2px(int spVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, + spVal, getResources().getDisplayMetrics()); + } +} diff --git a/app/src/main/res/layout/activity_drag_ball.xml b/app/src/main/res/layout/activity_drag_ball.xml new file mode 100644 index 0000000..9284726 --- /dev/null +++ b/app/src/main/res/layout/activity_drag_ball.xml @@ -0,0 +1,48 @@ + + + + + + + +