PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/app/src/main/java/com/thanhbc/instamaterial/views/adapters/FeedItemAnimator.java

https://gitlab.com/thanhbc/instamaterial
Java | 251 lines | 207 code | 41 blank | 3 comment | 15 complexity | 5e8e027eef262efaf6f6cbad06fda82e MD5 | raw file
  1. package com.thanhbc.instamaterial.views.adapters;
  2. import android.animation.Animator;
  3. import android.animation.AnimatorListenerAdapter;
  4. import android.animation.AnimatorSet;
  5. import android.animation.ObjectAnimator;
  6. import android.support.annotation.NonNull;
  7. import android.support.v7.widget.DefaultItemAnimator;
  8. import android.support.v7.widget.RecyclerView;
  9. import android.view.View;
  10. import android.view.animation.AccelerateInterpolator;
  11. import android.view.animation.DecelerateInterpolator;
  12. import android.view.animation.OvershootInterpolator;
  13. import com.thanhbc.instamaterial.R;
  14. import com.thanhbc.instamaterial.Utils;
  15. import java.util.HashMap;
  16. import java.util.List;
  17. import java.util.Map;
  18. /**
  19. * Created by thanhbc on 1/29/16.
  20. */
  21. public class FeedItemAnimator extends DefaultItemAnimator {
  22. private static final DecelerateInterpolator DECCELERATE_INTERPOLATOR = new DecelerateInterpolator();
  23. private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
  24. private static final OvershootInterpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(4);
  25. Map<RecyclerView.ViewHolder, AnimatorSet> likeAnimationsMap = new HashMap<>();
  26. Map<RecyclerView.ViewHolder, AnimatorSet> heartAnimationsMap = new HashMap<>();
  27. private int lastAddAnimatedItem = -2;
  28. @Override
  29. public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
  30. return true;
  31. }
  32. @NonNull
  33. @Override
  34. public ItemHolderInfo recordPreLayoutInformation(@NonNull RecyclerView.State state,
  35. @NonNull RecyclerView.ViewHolder viewHolder,
  36. int changeFlags, @NonNull List<Object> payloads) {
  37. if (changeFlags == FLAG_CHANGED) {
  38. for (Object payload : payloads) {
  39. if (payload instanceof String) {
  40. return new FeedItemHolderInfo((String) payload);
  41. }
  42. }
  43. }
  44. return super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads);
  45. }
  46. @Override
  47. public boolean animateAdd(RecyclerView.ViewHolder viewHolder) {
  48. if (viewHolder.getItemViewType() == FeedAdapter.VIEW_TYPE_DEFAULT) {
  49. if (viewHolder.getLayoutPosition() > lastAddAnimatedItem) {
  50. lastAddAnimatedItem++;
  51. runEnterAnimation((FeedAdapter.CellFeedViewHolder) viewHolder);
  52. return false;
  53. }
  54. }
  55. dispatchAddFinished(viewHolder);
  56. return false;
  57. }
  58. private void runEnterAnimation(final FeedAdapter.CellFeedViewHolder holder) {
  59. final int screenHeight = Utils.getScreenHeight(holder.itemView.getContext());
  60. holder.itemView.setTranslationY(screenHeight);
  61. holder.itemView.animate()
  62. .translationY(0)
  63. .setInterpolator(new DecelerateInterpolator(3.f))
  64. .setDuration(700)
  65. .setListener(new AnimatorListenerAdapter() {
  66. @Override
  67. public void onAnimationEnd(Animator animation) {
  68. dispatchAddFinished(holder);
  69. }
  70. })
  71. .start();
  72. }
  73. @Override
  74. public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder,
  75. @NonNull RecyclerView.ViewHolder newHolder,
  76. @NonNull ItemHolderInfo preInfo,
  77. @NonNull ItemHolderInfo postInfo) {
  78. cancelCurrentAnimationIfExists(newHolder);
  79. if (preInfo instanceof FeedItemHolderInfo) {
  80. FeedItemHolderInfo feedItemHolderInfo = (FeedItemHolderInfo) preInfo;
  81. FeedAdapter.CellFeedViewHolder holder = (FeedAdapter.CellFeedViewHolder) newHolder;
  82. if(holder.getFeedItem().isLiked) {
  83. animateHeartButton(holder);
  84. }else{
  85. holder.btnLike.setImageResource(R.mipmap.ic_heart_outline_grey);
  86. }
  87. updateLikesCounter(holder, holder.getFeedItem().likesCount);
  88. if (FeedAdapter.ACTION_LIKE_IMAGE_CLICKED.equals(feedItemHolderInfo.updateAction)) {
  89. animatePhotoLike(holder);
  90. }
  91. }
  92. return false;
  93. }
  94. private void cancelCurrentAnimationIfExists(RecyclerView.ViewHolder item) {
  95. if (likeAnimationsMap.containsKey(item)) {
  96. likeAnimationsMap.get(item).cancel();
  97. }
  98. if (heartAnimationsMap.containsKey(item)) {
  99. heartAnimationsMap.get(item).cancel();
  100. }
  101. }
  102. private void animateHeartButton(final FeedAdapter.CellFeedViewHolder holder) {
  103. AnimatorSet animatorSet = new AnimatorSet();
  104. ObjectAnimator rotationAnim = ObjectAnimator.ofFloat(holder.btnLike, "rotation", 0f, 360f);
  105. rotationAnim.setDuration(300);
  106. rotationAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
  107. ObjectAnimator bounceAnimX = ObjectAnimator.ofFloat(holder.btnLike, "scaleX", 0.2f, 1f);
  108. bounceAnimX.setDuration(300);
  109. bounceAnimX.setInterpolator(OVERSHOOT_INTERPOLATOR);
  110. ObjectAnimator bounceAnimY = ObjectAnimator.ofFloat(holder.btnLike, "scaleY", 0.2f, 1f);
  111. bounceAnimY.setDuration(300);
  112. bounceAnimY.setInterpolator(OVERSHOOT_INTERPOLATOR);
  113. bounceAnimY.addListener(new AnimatorListenerAdapter() {
  114. @Override
  115. public void onAnimationStart(Animator animation) {
  116. holder.btnLike.setImageResource(R.mipmap.ic_heart_red);
  117. }
  118. @Override
  119. public void onAnimationEnd(Animator animation) {
  120. heartAnimationsMap.remove(holder);
  121. dispatchChangeFinishedIfAllAnimationsEnded(holder);
  122. }
  123. });
  124. animatorSet.play(bounceAnimX).with(bounceAnimY).after(rotationAnim);
  125. animatorSet.start();
  126. heartAnimationsMap.put(holder, animatorSet);
  127. }
  128. private void updateLikesCounter(FeedAdapter.CellFeedViewHolder holder, int toValue) {
  129. String likesCountTextFrom = holder.tsLikesCounter.getResources().getQuantityString(
  130. R.plurals.likes_count, holder.getFeedItem().isLiked?toValue-1:toValue + 1, holder.getFeedItem().isLiked?toValue-1:toValue + 1
  131. );
  132. holder.tsLikesCounter.setCurrentText(likesCountTextFrom);
  133. String likesCountTextTo = holder.tsLikesCounter.getResources().getQuantityString(
  134. R.plurals.likes_count, toValue, toValue
  135. );
  136. holder.tsLikesCounter.setText(likesCountTextTo);
  137. }
  138. private void animatePhotoLike(final FeedAdapter.CellFeedViewHolder holder) {
  139. holder.vBgLike.setVisibility(View.VISIBLE);
  140. holder.ivLike.setVisibility(View.VISIBLE);
  141. holder.vBgLike.setScaleY(0.1f);
  142. holder.vBgLike.setScaleX(0.1f);
  143. holder.vBgLike.setAlpha(1f);
  144. holder.ivLike.setScaleY(0.1f);
  145. holder.ivLike.setScaleX(0.1f);
  146. AnimatorSet animatorSet = new AnimatorSet();
  147. ObjectAnimator bgScaleYAnim = ObjectAnimator.ofFloat(holder.vBgLike, "scaleY", 0.1f, 1f);
  148. bgScaleYAnim.setDuration(200);
  149. bgScaleYAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
  150. ObjectAnimator bgScaleXAnim = ObjectAnimator.ofFloat(holder.vBgLike, "scaleX", 0.1f, 1f);
  151. bgScaleXAnim.setDuration(200);
  152. bgScaleXAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
  153. ObjectAnimator bgAlphaAnim = ObjectAnimator.ofFloat(holder.vBgLike, "alpha", 1f, 0f);
  154. bgAlphaAnim.setDuration(200);
  155. bgAlphaAnim.setStartDelay(150);
  156. bgAlphaAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
  157. ObjectAnimator imgScaleUpYAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleY", 0.1f, 1f);
  158. imgScaleUpYAnim.setDuration(300);
  159. imgScaleUpYAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
  160. ObjectAnimator imgScaleUpXAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleX", 0.1f, 1f);
  161. imgScaleUpXAnim.setDuration(300);
  162. imgScaleUpXAnim.setInterpolator(DECCELERATE_INTERPOLATOR);
  163. ObjectAnimator imgScaleDownYAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleY", 1f, 0f);
  164. imgScaleDownYAnim.setDuration(300);
  165. imgScaleDownYAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
  166. ObjectAnimator imgScaleDownXAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleX", 1f, 0f);
  167. imgScaleDownXAnim.setDuration(300);
  168. imgScaleDownXAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
  169. animatorSet.playTogether(bgScaleYAnim, bgScaleXAnim, bgAlphaAnim, imgScaleUpYAnim, imgScaleUpXAnim);
  170. animatorSet.play(imgScaleDownYAnim).with(imgScaleDownXAnim).after(imgScaleUpYAnim);
  171. animatorSet.addListener(new AnimatorListenerAdapter() {
  172. @Override
  173. public void onAnimationEnd(Animator animation) {
  174. likeAnimationsMap.remove(holder);
  175. resetLikeAnimationState(holder);
  176. dispatchChangeFinishedIfAllAnimationsEnded(holder);
  177. }
  178. });
  179. animatorSet.start();
  180. likeAnimationsMap.put(holder, animatorSet);
  181. }
  182. private void dispatchChangeFinishedIfAllAnimationsEnded(FeedAdapter.CellFeedViewHolder holder) {
  183. if (likeAnimationsMap.containsKey(holder) || heartAnimationsMap.containsKey(holder)) {
  184. return;
  185. }
  186. dispatchAnimationFinished(holder);
  187. }
  188. private void resetLikeAnimationState(FeedAdapter.CellFeedViewHolder holder) {
  189. holder.vBgLike.setVisibility(View.INVISIBLE);
  190. holder.ivLike.setVisibility(View.INVISIBLE);
  191. }
  192. @Override
  193. public void endAnimation(RecyclerView.ViewHolder item) {
  194. super.endAnimation(item);
  195. cancelCurrentAnimationIfExists(item);
  196. }
  197. @Override
  198. public void endAnimations() {
  199. super.endAnimations();
  200. for (AnimatorSet animatorSet : likeAnimationsMap.values()) {
  201. animatorSet.cancel();
  202. }
  203. }
  204. public static class FeedItemHolderInfo extends ItemHolderInfo {
  205. public String updateAction;
  206. public FeedItemHolderInfo(String updateAction) {
  207. this.updateAction = updateAction;
  208. }
  209. }
  210. }