Advertisement
Guest User

Untitled

a guest
Nov 14th, 2018
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.91 KB | None | 0 0
  1. package al.easypay.epcheckout;
  2.  
  3. import android.animation.ValueAnimator;
  4. import android.annotation.TargetApi;
  5. import android.content.Context;
  6. import android.content.res.TypedArray;
  7. import android.graphics.Canvas;
  8. import android.graphics.Paint;
  9. import android.graphics.Path;
  10. import android.graphics.PathMeasure;
  11. import android.graphics.PointF;
  12. import android.graphics.RectF;
  13. import android.os.Build;
  14. import android.support.annotation.ColorInt;
  15. import android.support.annotation.FloatRange;
  16. import android.support.annotation.Nullable;
  17. import android.support.v4.view.animation.FastOutSlowInInterpolator;
  18. import android.util.AttributeSet;
  19. import android.util.Log;
  20. import android.view.View;
  21. import android.view.animation.AccelerateInterpolator;
  22. import android.view.animation.Interpolator;
  23. import android.view.animation.PathInterpolator;
  24.  
  25. /**
  26. * Animating check mark.
  27. */
  28. class CheckView extends View {
  29.  
  30. private static final String TAG = CheckView.class.getSimpleName();
  31. private static final boolean DEBUG = false;
  32. private static final long CHECK_ANIM_DURATION = 300L;
  33. private static final long SCALE_ANIM_DELAY = 280L;
  34. private static final long SCALE_ANIM_DURATION = 250L;
  35. private static final float DEFAULT_STROKE_WIDTH = 8F;
  36. private static final int DEFAULT_STROKE_COLOR = 0xFFFFFF; // white
  37. private static final float SCALE_MIN = 0.80F;
  38.  
  39. private Interpolator mCheckInterpolator;
  40. /**
  41. * The path of the circle around the check mark
  42. */
  43. private Path mPathCircle;
  44. /**
  45. * The path of the check mark
  46. */
  47. private Path mPathCheck;
  48. /**
  49. * The length of the start of the check mark, before the pivot point
  50. */
  51. private float mMinorContourLength;
  52. /**
  53. * The length of the check mark after the pivot point, and up to the end point.
  54. */
  55. private float mMajorContourLength;
  56. /**
  57. * The size of the check mark and circle paths.
  58. */
  59. private float mStrokeWidth = DEFAULT_STROKE_WIDTH;
  60. private int mStrokeColor = DEFAULT_STROKE_COLOR;
  61. /**
  62. * A Rect describing the area on this View's canvas where the check mark should be drawn.
  63. * This is intended to account for padding.
  64. */
  65. private RectF mDrawingRect;
  66. /**
  67. * A Rect describing the drawable area for the circle around the check mark.
  68. * This takes into account the extra room needed for the stroke width.
  69. */
  70. private RectF mCircleRect;
  71. private Paint mPaint;
  72. private PathMeasure mPathMeasure;
  73. /**
  74. * A pre-allocated float array to hold path measure results.
  75. */
  76. private float[] mPoint;
  77. /**
  78. * Where the check mark starts
  79. */
  80. private PointF mCheckStart;
  81. /**
  82. * Where the check mark turns upward
  83. */
  84. private PointF mCheckPivot;
  85. /**
  86. * Where the check mark ends
  87. */
  88. private PointF mCheckEnd;
  89. /**
  90. * Where the circle border starts
  91. */
  92. private PointF mCircleStart;
  93. private ValueAnimator mCheckAnimator;
  94. private ValueAnimator mCircleAnimator;
  95. private ValueAnimator mScaleAnimator;
  96. private boolean mChecked = false;
  97.  
  98. CheckView(Context context) {
  99. super(context);
  100. init(context, null);
  101. }
  102.  
  103. CheckView(Context context, AttributeSet attrs) {
  104. super(context, attrs);
  105. init(context, attrs);
  106. }
  107.  
  108. CheckView(Context context, AttributeSet attrs, int defStyleAttr) {
  109. super(context, attrs, defStyleAttr);
  110. init(context, attrs);
  111. }
  112.  
  113. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  114. CheckView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  115. super(context, attrs, defStyleAttr, defStyleRes);
  116. init(context, attrs);
  117. }
  118.  
  119. private void init(Context context, @Nullable AttributeSet attrs) {
  120. resolveAttributes(context, attrs);
  121. mPathCheck = new Path();
  122. mPathCircle = new Path();
  123. mDrawingRect = new RectF();
  124. mCircleRect = new RectF();
  125. mPathMeasure = new PathMeasure();
  126. mPoint = new float[2];
  127. mCheckStart = new PointF();
  128. mCheckPivot = new PointF();
  129. mCheckEnd = new PointF();
  130. mCircleStart = new PointF();
  131. mCheckAnimator = ValueAnimator.ofFloat(0, 1);
  132. mCircleAnimator = ValueAnimator.ofFloat(0, 1);
  133. mScaleAnimator = ValueAnimator.ofFloat(1, SCALE_MIN, 1);
  134. mCheckInterpolator = createCheckInterpolatorCompat();
  135. mPaint = createPaint(mStrokeColor, mStrokeWidth);
  136. }
  137.  
  138. private void resolveAttributes(Context c, @Nullable AttributeSet attrs) {
  139. if (attrs == null) {
  140. return;
  141. }
  142.  
  143. TypedArray a = c.getTheme().obtainStyledAttributes(attrs, R.styleable.CheckView, 0, 0);
  144.  
  145. try {
  146. mStrokeWidth = a.getDimension(R.styleable.CheckView_checkView_strokeWidth, DEFAULT_STROKE_WIDTH);
  147. mStrokeColor = a.getColor(R.styleable.CheckView_checkView_strokeColor, DEFAULT_STROKE_COLOR);
  148. } finally {
  149. a.recycle();
  150. }
  151. }
  152.  
  153. @Override
  154. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  155. super.onLayout(changed, left, top, right, bottom);
  156. if (changed) {
  157. mDrawingRect.left = getPaddingLeft();
  158. mDrawingRect.top = getPaddingTop();
  159. mDrawingRect.right = getMeasuredWidth() - getPaddingRight();
  160. mDrawingRect.bottom = getMeasuredHeight() - getPaddingBottom();
  161.  
  162. mCheckStart.x = mDrawingRect.left + mDrawingRect.width() / 4;
  163. mCheckStart.y = mDrawingRect.top + mDrawingRect.height() / 2;
  164. mCheckPivot.x = mDrawingRect.left + mDrawingRect.width() * .426F;
  165. mCheckPivot.y = mDrawingRect.top + mDrawingRect.height() * .66F;
  166. mCheckEnd.x = mDrawingRect.left + mDrawingRect.width() * .75F;
  167. mCheckEnd.y = mDrawingRect.top + mDrawingRect.height() * .30F;
  168.  
  169. mMinorContourLength = distance(mCheckStart.x, mCheckStart.y, mCheckPivot.x, mCheckPivot.y);
  170. mMajorContourLength = distance(mCheckPivot.x, mCheckPivot.y, mCheckEnd.x, mCheckEnd.y);
  171.  
  172. mCircleRect.left = mDrawingRect.left + mStrokeWidth /2;
  173. mCircleRect.top = mDrawingRect.top + mStrokeWidth /2;
  174. mCircleRect.right = mDrawingRect.right - mStrokeWidth /2;
  175. mCircleRect.bottom = mDrawingRect.bottom - mStrokeWidth /2;
  176. mCircleStart.x = mCircleRect.right;
  177. mCircleStart.y = mCircleRect.bottom /2;
  178.  
  179. if (DEBUG && (mDrawingRect.width() != mDrawingRect.height())) {
  180. Log.w(TAG, "WARNING: " + CheckView.class.getSimpleName() + " will look weird because you've given it a non-square drawing area. " +
  181. "Make sure the width, height, and padding resolve to a square.");
  182. }
  183. }
  184. }
  185.  
  186. @Override
  187. protected void onDraw(Canvas canvas) {
  188. super.onDraw(canvas);
  189. if (!mChecked) {
  190. return;
  191. }
  192. canvas.drawPath(mPathCheck, mPaint);
  193. canvas.drawPath(mPathCircle, mPaint);
  194. }
  195.  
  196. //region instance methods
  197.  
  198. /**
  199. * Tell this {@link CheckView} to animate into the checked state.
  200. */
  201. void check() {
  202. mChecked = true;
  203. mCheckAnimator.removeAllUpdateListeners();
  204. mCheckAnimator.setDuration(CHECK_ANIM_DURATION)
  205. .setInterpolator(mCheckInterpolator);
  206. mCheckAnimator.addUpdateListener(mCheckAnimatorListener);
  207.  
  208. mCircleAnimator.removeAllUpdateListeners();
  209. mCircleAnimator.setDuration(CHECK_ANIM_DURATION)
  210. .setInterpolator(mCheckInterpolator);
  211. mCircleAnimator.addUpdateListener(mCircleAnimatorListener);
  212.  
  213. mScaleAnimator.removeAllUpdateListeners();
  214. mScaleAnimator.setDuration(SCALE_ANIM_DURATION)
  215. .setStartDelay(SCALE_ANIM_DELAY);
  216. mScaleAnimator.setInterpolator(new FastOutSlowInInterpolator());
  217. mScaleAnimator.addUpdateListener(mScaleAnimatorListener);
  218.  
  219. mCheckAnimator.start();
  220. mCircleAnimator.start();
  221. mScaleAnimator.start();
  222. }
  223.  
  224. /**
  225. * Reset to an unchecked state. This will not animate.
  226. */
  227. void uncheck() {
  228. mChecked = false;
  229. invalidate();
  230. }
  231. //endregion instance methods
  232.  
  233. //region private methods
  234. private Paint createPaint(@ColorInt int color, float strokeWidth) {
  235. Paint p = new Paint();
  236. p.setColor(color);
  237. p.setStyle(Paint.Style.STROKE);
  238. p.setStrokeWidth(strokeWidth);
  239. p.setStrokeJoin(Paint.Join.ROUND);
  240. p.setAntiAlias(true);
  241. p.setStrokeCap(Paint.Cap.ROUND);
  242. return p;
  243. }
  244.  
  245. private Interpolator createCheckInterpolatorCompat() {
  246. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  247. return new PathInterpolator(0.755F, 0.05F, 0.855F, 0.06F);
  248. } else {
  249. return new AccelerateInterpolator();
  250. }
  251. }
  252.  
  253. /**
  254. * What does the check mark path look like at it's full length?
  255. */
  256. private void setCheckPathFull() {
  257. mPathCheck.reset();
  258. mPathCheck.moveTo(mCheckStart.x, mCheckStart.y);
  259. mPathCheck.lineTo(mCheckPivot.x, mCheckPivot.y);
  260. mPathCheck.lineTo(mCheckEnd.x, mCheckEnd.y);
  261. }
  262.  
  263. /**
  264. * What does the check mark path look like at {@code percent} of it's total length?
  265. */
  266. private void setCheckPathPercentage(@FloatRange(from = 0, to = 1) float percent) {
  267. setCheckPathFull();
  268. final float totalLength = mMinorContourLength + mMajorContourLength;
  269. final float pivotPercent = mMinorContourLength / totalLength;
  270.  
  271. // TODO: try this with a simple getSegment();
  272. if (percent > pivotPercent) {
  273. final float remainder = percent - pivotPercent;
  274. final float distance = totalLength * remainder;
  275. mPathCheck.reset();
  276. mPathCheck.moveTo(mCheckPivot.x, mCheckPivot.y);
  277. mPathCheck.lineTo(mCheckEnd.x, mCheckEnd.y);
  278. mPathMeasure.setPath(mPathCheck, false);
  279. mPathMeasure.getPosTan(distance, mPoint, null);
  280. mPathCheck.reset();
  281. mPathCheck.moveTo(mCheckStart.x, mCheckStart.y);
  282. mPathCheck.lineTo(mCheckPivot.x, mCheckPivot.y);
  283. mPathCheck.lineTo(mPoint[0], mPoint[1]);
  284. } else if (percent < pivotPercent) {
  285. final float minorPercent = percent / pivotPercent;
  286. final float distance = mMinorContourLength * minorPercent;
  287. mPathMeasure.setPath(mPathCheck, false);
  288. mPathMeasure.getPosTan(distance, mPoint, null);
  289. mPathCheck.reset();
  290. mPathCheck.moveTo(mCheckStart.x, mCheckStart.y);
  291. mPathCheck.lineTo(mPoint[0], mPoint[1]);
  292. } else if (percent == pivotPercent) {
  293. mPathCheck.lineTo(mCheckPivot.x, mCheckPivot.y);
  294. }
  295. }
  296.  
  297. private void setCirclePathPercentage(@FloatRange(from = 0, to = 1) float percent) {
  298. mPathCircle.reset();
  299. mPathCircle.moveTo(mCircleStart.x, mCircleStart.y);
  300. mPathCircle.addArc(mCircleRect, 0, 360);
  301.  
  302. mPathMeasure.setPath(mPathCircle, false);
  303. final float distance = mPathMeasure.getLength() * percent;
  304. mPathMeasure.getPosTan(distance, mPoint, null);
  305. mPathCircle.reset();
  306. mPathCircle.moveTo(mCircleStart.x, mCircleStart.y);
  307. mPathCircle.arcTo(mCircleRect, 0, (359 * percent));
  308. }
  309.  
  310. private static float distance(float x1, float y1, float x2, float y2) {
  311. final float xAbs = Math.abs(x1 - x2);
  312. final float yAbs = Math.abs(y1 - y2);
  313. return (float) Math.sqrt((yAbs * yAbs) + (xAbs * xAbs));
  314. }
  315. //endregion private methods
  316.  
  317. //region animator listeners
  318. private final ValueAnimator.AnimatorUpdateListener mCheckAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
  319. @Override
  320. public void onAnimationUpdate(ValueAnimator animation) {
  321. final float fraction = animation.getAnimatedFraction();
  322. setCheckPathPercentage(fraction);
  323. invalidate();
  324. }
  325. };
  326.  
  327. private final ValueAnimator.AnimatorUpdateListener mCircleAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
  328. @Override
  329. public void onAnimationUpdate(ValueAnimator animation) {
  330. final float fraction = animation.getAnimatedFraction();
  331. setCirclePathPercentage(fraction);
  332. invalidate();
  333. }
  334. };
  335.  
  336. private final ValueAnimator.AnimatorUpdateListener mScaleAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
  337. @Override
  338. public void onAnimationUpdate(ValueAnimator animation) {
  339. final float value = (float) animation.getAnimatedValue();
  340. CheckView.this.setScaleX(value);
  341. CheckView.this.setScaleY(value);
  342. invalidate();
  343. }
  344. };
  345. //endregion
  346. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement