UnlockView.java 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. package com.ht.gate;
  2. import android.annotation.SuppressLint;
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.graphics.Bitmap;
  6. import android.graphics.BitmapFactory;
  7. import android.graphics.Canvas;
  8. import android.graphics.Color;
  9. import android.graphics.DrawFilter;
  10. import android.graphics.LinearGradient;
  11. import android.graphics.Paint;
  12. import android.graphics.PaintFlagsDrawFilter;
  13. import android.graphics.Path;
  14. import android.graphics.Rect;
  15. import android.graphics.RectF;
  16. import android.graphics.Shader;
  17. import android.graphics.Typeface;
  18. import android.text.TextUtils;
  19. import android.util.AttributeSet;
  20. import android.util.Log;
  21. import android.util.TypedValue;
  22. import android.view.MotionEvent;
  23. import android.view.View;
  24. import androidx.annotation.Nullable;
  25. import androidx.core.content.res.ResourcesCompat;
  26. public class UnlockView extends View {
  27. private Paint borderPaint;
  28. private Paint linePaint;
  29. private Paint textPaint;
  30. private Shader lineShader;
  31. private Path borderPath;
  32. private float borderWidth;
  33. private Bitmap bmUnlock;
  34. private Bitmap bmUnlocked;
  35. private float distance = 0;
  36. private float padding = 0;
  37. private boolean down;
  38. private float lastX;
  39. private Rect src, src1;
  40. private RectF dst, dst1;
  41. private DrawFilter drawFilter;
  42. private Typeface source_han_sans_sc_normal;
  43. private Typeface source_han_sans_sc_light;
  44. private int colorLight = 0xFFFFFFFF;
  45. private int colorDark = 0xFF222639;
  46. private int colorUnlockedLight = 0x11C1C3CD;
  47. private int colorUnlockedDark = 0x1E222639;
  48. private boolean dark;
  49. private boolean preUnlock = false;
  50. private String hint;
  51. private UnlockListener unlockListener;
  52. private boolean unlocked;
  53. public interface UnlockListener {
  54. void onUnlock();
  55. }
  56. public void setUnlockListener(UnlockListener unlockListener) {
  57. this.unlockListener = unlockListener;
  58. }
  59. public UnlockView(Context context) {
  60. this(context, null);
  61. }
  62. public UnlockView(Context context, @Nullable AttributeSet attrs) {
  63. this(context, attrs, 0);
  64. }
  65. public UnlockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  66. this(context, attrs, defStyleAttr, 0);
  67. }
  68. public UnlockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  69. super(context, attrs, defStyleAttr, defStyleRes);
  70. TypedArray t = getContext().obtainStyledAttributes(attrs,
  71. R.styleable.UnlockView);
  72. hint = t.getString(R.styleable.UnlockView_hint);
  73. if (TextUtils.isEmpty(hint)) {
  74. hint = "滑动解锁";
  75. }
  76. dark = t.getBoolean(R.styleable.UnlockView_darkMode, false);
  77. t.recycle();
  78. source_han_sans_sc_normal = ResourcesCompat.getFont(getContext(), R.font.source_han_sans_sc_normal);
  79. source_han_sans_sc_light = ResourcesCompat.getFont(getContext(), R.font.source_han_sans_sc_light);
  80. borderPaint = new Paint();
  81. borderPaint.setAntiAlias(true);
  82. borderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getContext().getResources().getDisplayMetrics());
  83. borderPaint.setStrokeWidth(borderWidth);
  84. borderPaint.setStyle(Paint.Style.STROKE);
  85. borderPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
  86. borderPath = new Path();
  87. padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 7, getContext().getResources().getDisplayMetrics());
  88. bmUnlock = BitmapFactory.decodeResource(getResources(), dark ? R.mipmap.icon_unlock_dark : R.mipmap.icon_unlock);
  89. bmUnlocked = BitmapFactory.decodeResource(getResources(), dark ? R.mipmap.icon_unlocked_dark : R.mipmap.icon_unlocked);
  90. src = new Rect(0, 0, bmUnlock.getWidth(), bmUnlock.getHeight());
  91. dst = new RectF();
  92. src1 = new Rect(0, 0, bmUnlocked.getWidth(), bmUnlocked.getHeight());
  93. dst1 = new RectF();
  94. drawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
  95. linePaint = new Paint();
  96. linePaint.setAntiAlias(true);
  97. linePaint.setStrokeCap(Paint.Cap.ROUND);
  98. textPaint = new Paint();
  99. textPaint.setAntiAlias(true);
  100. textPaint.setTextSize(sp2px(25));
  101. textPaint.setStyle(Paint.Style.FILL);
  102. textPaint.setTypeface(source_han_sans_sc_light);
  103. }
  104. public void setUnlocked(boolean unlocked) {
  105. this.unlocked = unlocked;
  106. postInvalidate();
  107. }
  108. @Override
  109. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  110. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  111. }
  112. @SuppressLint("DrawAllocation")
  113. @Override
  114. protected void onDraw(Canvas canvas) {
  115. canvas.setDrawFilter(drawFilter);
  116. float width = getWidth();
  117. float height = getHeight();
  118. float d = (height - borderWidth);
  119. float r = d / 2;
  120. if (unlocked) {
  121. borderPaint.setColor(dark ? colorUnlockedDark : colorUnlockedLight);
  122. borderPaint.setStyle(Paint.Style.FILL);
  123. textPaint.setColor(dark ? colorDark : colorLight);
  124. canvas.drawRoundRect(0, 0, width, height, height / 2, height / 2, borderPaint);
  125. float textWidth = textPaint.measureText("已解锁");
  126. float baseLineY = Math.abs(textPaint.ascent() + textPaint.descent()) / 2 + getHeight() / 2f;
  127. canvas.drawText("已解锁", (getWidth() + dp2px(32)) / 2f - textWidth / 2, baseLineY, textPaint);
  128. dst1.left = (width - textPaint.measureText("已解锁") - dp2px(32)) / 2;
  129. dst1.top = (height - dp2px(26)) / 2;
  130. dst1.right = dst1.left + dp2px(21);
  131. dst1.bottom = dst1.top + dp2px(26);
  132. canvas.drawBitmap(bmUnlocked, src1, dst1, null);
  133. return;
  134. } else {
  135. borderPaint.setStyle(Paint.Style.STROKE);
  136. if (dark) {
  137. borderPaint.setColor(colorDark);
  138. textPaint.setColor(colorDark);
  139. } else {
  140. borderPaint.setColor(colorLight);
  141. textPaint.setColor(colorLight);
  142. }
  143. }
  144. borderPath.reset();
  145. borderPath.moveTo(r + borderWidth / 2, borderWidth / 2);
  146. borderPath.lineTo(r + borderWidth / 2, borderWidth / 2);
  147. borderPath.lineTo(width - r, borderWidth / 2);
  148. borderPath.arcTo(width - d - borderWidth / 2, borderWidth / 2, width - borderWidth / 2, height - borderWidth / 2, -90, 180, true);
  149. borderPath.lineTo(r + borderWidth / 2, height - borderWidth / 2);
  150. borderPath.arcTo(borderWidth / 2, borderWidth / 2, d + borderWidth / 2, height - borderWidth / 2, 90, 180, true);
  151. canvas.drawPath(borderPath, borderPaint);
  152. float size = height - padding * 2;
  153. int dd = (int) Math.max(0, Math.min(distance, getWidth() - padding * 2 - size));
  154. linePaint.setStrokeWidth(height - padding * 2);
  155. lineShader = dark ? new LinearGradient(0, 0, width, 0,
  156. new int[]{0x00222639,
  157. 0x1F222639,
  158. 0x78222639,
  159. 0xD4222639},
  160. new float[]{0, 0.19f, 0.42f, 1}, Shader.TileMode.CLAMP)
  161. : new LinearGradient(0, 0, width, 0,
  162. new int[]{Color.argb(0, 255, 255, 255),
  163. Color.argb(30, 255, 255, 255),
  164. Color.argb(117, 255, 255, 255),
  165. Color.argb(255, 255, 255, 255)},
  166. new float[]{0, 0.19f, 0.42f, 1}, Shader.TileMode.CLAMP);
  167. linePaint.setShader(lineShader);
  168. canvas.drawLine(r, height / 2, r + dd, height / 2, linePaint);
  169. drawCenterText(canvas, textPaint, hint);
  170. dst.left = (int) padding + dd;
  171. dst.top = (int) padding;
  172. dst.right = (int) (padding + size + dd);
  173. dst.bottom = (int) (padding + size);
  174. canvas.drawBitmap(bmUnlock, src, dst, null);
  175. }
  176. @Override
  177. public boolean onTouchEvent(MotionEvent event) {
  178. switch (event.getAction()) {
  179. case MotionEvent.ACTION_DOWN:
  180. if (event.getX() < getHeight() && event.getY() < getHeight()) {
  181. down = true;
  182. lastX = event.getX();
  183. }
  184. break;
  185. case MotionEvent.ACTION_UP:
  186. distance = 0;
  187. down = false;
  188. preUnlock = false;
  189. postInvalidate();
  190. break;
  191. case MotionEvent.ACTION_MOVE:
  192. if (down) {
  193. distance += event.getX() - lastX;
  194. lastX = event.getX();
  195. if (distance >= getWidth() - padding * 2 - (getHeight() - padding * 2) && !preUnlock) {
  196. preUnlock = true;
  197. if (unlockListener != null) {
  198. unlockListener.onUnlock();
  199. }
  200. }
  201. postInvalidate();
  202. }
  203. break;
  204. }
  205. return true;
  206. }
  207. @Override
  208. protected void onDetachedFromWindow() {
  209. super.onDetachedFromWindow();
  210. if (bmUnlock != null && !bmUnlock.isRecycled()) {
  211. bmUnlock.recycle();
  212. bmUnlock = null;
  213. }
  214. }
  215. private void drawCenterText(Canvas canvas, Paint paint, String text) {
  216. // 文本宽
  217. float textWidth = paint.measureText(text);
  218. // 文本baseline在y轴方向的位置
  219. float baseLineY = Math.abs(paint.ascent() + paint.descent()) / 2 + getHeight() / 2f;
  220. canvas.drawText(text, getWidth() / 2f - textWidth / 2, baseLineY, paint);
  221. }
  222. public int sp2px(float sp) {
  223. return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
  224. getContext().getResources().getDisplayMetrics());
  225. }
  226. public int dp2px(float dp) {
  227. return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
  228. getContext().getResources().getDisplayMetrics());
  229. }
  230. }