小言_互联网的博客

安卓Android 直播点赞爱心特效,计时器

429人阅读  评论(0)

 

点赞特效,上图:

首先忽略这画质和抠脚的交互效果,首先需求就是 实现类似抖音的点赞效果 飘小心心的效果,UI的方案是做成了gif图,但是这种东西做成gif太low了,于是就有了想法,这边记录一下:

首先 图形是随机产生,这个不多说,ui的图片 然后点赞随机飘爱心的效果要怎么实现呢,想到的是 Android 自定义属性动画 ,那就用这个方式来实现吧,用自定义view的方式实现它,实现如下:

(爱心图标文件来源:IconFont  阿里巴巴矢量图标库 )

(源码下载地址在最后面)

你好sao啊!

 

 


   
  1. package com.dpdp.base_moudle.weight;
  2. import android.animation.Animator;
  3. import android.animation.AnimatorListenerAdapter;
  4. import android.animation.AnimatorSet;
  5. import android.animation.ObjectAnimator;
  6. import android.animation.TypeEvaluator;
  7. import android.animation.ValueAnimator;
  8. import android.content.Context;
  9. import android.graphics.PointF;
  10. import android.graphics.drawable.Drawable;
  11. import android.util.AttributeSet;
  12. import android.view.Gravity;
  13. import android.view.View;
  14. import android.view.animation.LinearInterpolator;
  15. import android.widget.FrameLayout;
  16. import android.widget.ImageView;
  17. import androidx.annotation.NonNull;
  18. import androidx.annotation.Nullable;
  19. import androidx.core.content.ContextCompat;
  20. import com.dpdp.base_moudle.R;
  21. import java.util.ArrayList;
  22. import java.util.Random;
  23. /**
  24. * Created by ldp.
  25. * <p>
  26. * Date: 2021-02-23
  27. * <p>
  28. * Summary: 点赞爱心动画
  29. * <p>
  30. * 固定宽高
  31. * <p>
  32. * 包含一个子view 或者没有 要放在最下面 动效爱心往上飘
  33. * <p>
  34. * @see #setLikeDrawables(int...) 设置 对应的 图片
  35. * @see #clickLikeView() 点击调用 开始动画 点一次出一个
  36. * @see #autoPlayClickView(int, boolean) 自动播放动画
  37. * @see #stopAutoPlay() 停止自动播放
  38. * @see #release() 释放资源 退出时调用
  39. */
  40. public class FloatLikeView extends FrameLayout {
  41. private ArrayList<Drawable> mLikeDrawables; // 点赞的drawable集合
  42. private Random mRandom; //产生随机数来产生随机爱心
  43. private final Context context;
  44. private int mLikePicWidth = 0; // 爱心的 view 宽
  45. private int mLikePicHeight = 0; // 爱心的 view 高
  46. private LayoutParams mLikePicLayoutParams; // 布局参数
  47. private int mLikePicBottomMargin = 0; // 爱心出现位置距离底部
  48. private int mChildHeight = 0; // 如果底部设置了一个 view 爱心会在其上面
  49. private int mWidth; // 此布局view 宽
  50. private int mHeight; // 此布局view 高
  51. private AnimatorSet animator; // 爱心动画
  52. public FloatLikeView(@NonNull Context context) {
  53. this(context, null);
  54. }
  55. public FloatLikeView(@NonNull Context context, @Nullable AttributeSet attrs) {
  56. this(context, attrs, 0);
  57. }
  58. public FloatLikeView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  59. super(context, attrs, defStyleAttr);
  60. this.context = context;
  61. init();
  62. }
  63. private void init() {
  64. // 存放 要显示的 效果
  65. mLikeDrawables = new ArrayList<>();
  66. // 设置一个默认的爱心
  67. mLikeDrawables.add(ContextCompat.getDrawable(context, R.drawable.ui_default_heart));
  68. // 产生随机数 随机选择 出现的图形
  69. mRandom = new Random();
  70. }
  71. /**
  72. * 设置 动画飘动的资源文件
  73. */
  74. public void setLikeDrawables(int... drawableIds) {
  75. if (drawableIds != null && drawableIds.length > 0) {
  76. mLikeDrawables.clear();
  77. for ( int drawableId : drawableIds) {
  78. mLikeDrawables.add(ContextCompat.getDrawable(context, drawableId));
  79. }
  80. }
  81. }
  82. private ValueAnimator valueAnimator;
  83. /**
  84. * 自动 播放 飘爱心 动画 利用属性动画 进行3秒的
  85. * <p>
  86. * 设置了最大 30 个 看情况自己设置好吧 太多了不好看
  87. *
  88. * @param likeCounts 飘心的数量
  89. * @param isRepeat 是否重复播放
  90. * 停止播放 {@link #stopAutoPlay()}
  91. */
  92. public void autoPlayClickView(int likeCounts, boolean isRepeat) {
  93. // 重复调用 则取消上次的重新开始
  94. if (valueAnimator != null) {
  95. valueAnimator.cancel();
  96. valueAnimator.removeAllUpdateListeners();
  97. valueAnimator = null;
  98. }
  99. valueAnimator = ValueAnimator.ofInt( 0, Math.min(likeCounts, 30)); //30
  100. valueAnimator.setDuration( 3000);
  101. valueAnimator.setInterpolator( new LinearInterpolator());
  102. // 是否 开启循环播放 -====
  103. valueAnimator.setRepeatCount(isRepeat ? ValueAnimator.INFINITE : 1);
  104. valueAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
  105. int lastValue = 0;
  106. @Override
  107. public void onAnimationUpdate(ValueAnimator animation) {
  108. int animatedValue = (( int) animation.getAnimatedValue());
  109. // 利用 动画的更新回调 相当于一个定时效果 但是要注意 会有多次相同的回调 要判断一下
  110. if (animatedValue == lastValue) return;
  111. // 手动调用一次 相当于点击一次 出一个爱心效果
  112. clickLikeView();
  113. // 记录上次的值
  114. lastValue = animatedValue;
  115. }
  116. });
  117. valueAnimator.start();
  118. }
  119. /**
  120. * 停止自动播放
  121. * <p>
  122. * {@link #autoPlayClickView(int, boolean)} 自动播放
  123. */
  124. public void stopAutoPlay() {
  125. if (valueAnimator != null) {
  126. valueAnimator.cancel();
  127. valueAnimator.removeAllUpdateListeners();
  128. }
  129. }
  130. /**
  131. * 退出时 释放资源
  132. */
  133. public void release() {
  134. stopAutoPlay();
  135. if (animator != null) {
  136. animator.cancel();
  137. animator.removeAllListeners();
  138. }
  139. }
  140. /**
  141. * 点击调用 产生动效 调用一次 产生一个爱心飘动效果
  142. */
  143. public void clickLikeView() {
  144. if (mLikePicWidth == 0 || mLikePicHeight == 0 || mLikePicLayoutParams == null) {
  145. // 获取 点赞的 view 尺寸 ,这边定一个 统一尺寸
  146. mLikePicWidth = mLikeDrawables.get( 0).getIntrinsicWidth();
  147. mLikePicHeight = mLikeDrawables.get( 0).getIntrinsicHeight();
  148. // 指定爱心出现的位置
  149. mLikePicLayoutParams = new LayoutParams(mLikePicWidth, mLikePicHeight);
  150. // 位置在底部的中间
  151. mLikePicLayoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
  152. // 如果底部有一个 childView 则会在其上方 飘爱心
  153. mLikePicLayoutParams.bottomMargin = mLikePicBottomMargin;
  154. }
  155. ImageView likeIv = new ImageView(context);
  156. likeIv.setImageDrawable(mLikeDrawables.get(mRandom.nextInt(mLikeDrawables.size())));
  157. likeIv.setLayoutParams(mLikePicLayoutParams);
  158. addView(likeIv);
  159. addAnimationStart(likeIv);
  160. }
  161. /**
  162. * 飘爱心动画 主要逻辑
  163. */
  164. private void addAnimationStart(ImageView likeIv) {
  165. //----------------------------------- 出现 的动画-----------------------------------
  166. ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(likeIv, "alpha", 0.5f, 1f);
  167. ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(likeIv, "scaleX", 0.6f, 1f);
  168. ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(likeIv, "scaleY", 0.6f, 1f);
  169. AnimatorSet animatorSet = new AnimatorSet();
  170. animatorSet.playTogether(alphaAnimator, scaleXAnimator, scaleYAnimator);
  171. animatorSet.setDuration( 200);
  172. animatorSet.setTarget(likeIv);
  173. //------------------------ 路径移动动画 基于 三阶贝塞尔曲线-------------------------
  174. PathEvaluator pathEvaluator = new PathEvaluator(getControlPointF( 1), getControlPointF( 2));
  175. // 设置 起点
  176. PointF startPointF = new PointF(( float) (mWidth - mLikePicWidth) / 2, mHeight - mLikePicBottomMargin - mLikePicHeight);
  177. // 设置 终点
  178. PointF endPointF = new PointF(( float) mWidth / 2 + (mRandom.nextBoolean() ? 1 : - 1) * mRandom.nextInt( 100), 0);
  179. // 自定义 属性动画 利用贝塞尔曲线 计算路径上的各个点的位置
  180. ValueAnimator valueAnimator = ValueAnimator.ofObject(pathEvaluator, startPointF, endPointF);
  181. valueAnimator.setDuration( 3000);
  182. valueAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
  183. @Override
  184. public void onAnimationUpdate(ValueAnimator animation) {
  185. if (likeIv == null) return;
  186. // 根据各个点的位置 改变 view 的位置
  187. PointF pointF = (PointF) animation.getAnimatedValue();
  188. likeIv.setX(pointF.x);
  189. likeIv.setY(pointF.y);
  190. // 根据 动画的进度 设置透明度
  191. likeIv.setAlpha( 1f - animation.getAnimatedFraction());
  192. }
  193. });
  194. valueAnimator.setTarget(likeIv);
  195. animator = new AnimatorSet();
  196. animator.setTarget(likeIv);
  197. // 动画顺序播放
  198. animator.playSequentially(animatorSet, valueAnimator);
  199. animator.addListener( new AnimatorListenerAdapter() {
  200. @Override
  201. public void onAnimationEnd(Animator animation) {
  202. // 动画结束 移除添加的 view
  203. // ~!!!
  204. // ~!!!
  205. // ~!!!
  206. removeView(likeIv);
  207. }
  208. });
  209. animator.start();
  210. }
  211. /**
  212. * 随机产生 三阶贝赛尔曲线 中间两个控制点
  213. *
  214. * @param value 控制点
  215. */
  216. private PointF getControlPointF(int value) {
  217. PointF pointF = new PointF();
  218. pointF.x = ( float) mWidth / 2 - mRandom.nextInt( 100);
  219. pointF.y = mRandom.nextInt((mHeight - mLikePicBottomMargin - mLikePicHeight) / value);
  220. return pointF;
  221. }
  222. @Override
  223. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  224. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  225. int childCount = getChildCount();
  226. // 包含一个子view 或者没有 要放在最下面
  227. if (mChildHeight == 0 && childCount > 0) {
  228. View child = getChildAt( 0);
  229. measureChild(child, widthMeasureSpec, heightMeasureSpec);
  230. mChildHeight = child.getMeasuredHeight();
  231. mLikePicBottomMargin = mChildHeight;
  232. }
  233. mHeight = getMeasuredHeight();
  234. mWidth = getMeasuredWidth();
  235. }
  236. private static class PathEvaluator implements TypeEvaluator<PointF> {
  237. private final PointF point01;
  238. private final PointF point02;
  239. public PathEvaluator(PointF point01, PointF point02) {
  240. this.point01 = point01;
  241. this.point02 = point02;
  242. }
  243. @Override
  244. public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
  245. float change = 1.0f - fraction;
  246. PointF pointF = new PointF();
  247. // 三阶贝塞儿曲线
  248. pointF.x = ( float) Math.pow(change, 3) * startValue.x
  249. + 3 * ( float) Math.pow(change, 2) * fraction * point01.x
  250. + 3 * change * ( float) Math.pow(fraction, 2) * point02.x
  251. + ( float) Math.pow(fraction, 3) * endValue.x;
  252. pointF.y = ( float) Math.pow(change, 3) * startValue.y
  253. + 3 * ( float) Math.pow(change, 2) * fraction * point01.y
  254. + 3 * change * fraction * fraction * point02.y
  255. + ( float) Math.pow(fraction, 3) * endValue.y;
  256. return pointF;
  257. }
  258. }
  259. }

使用方法:


   
  1. <com.dpdp.base_moudle.weight.FloatLikeView
  2. android:id= "@+id/like_btn_01"
  3. android:layout_width= "100dp"
  4. android:layout_height= "400dp">
  5. <Button
  6. android:id= "@+id/praise_01"
  7. android:layout_width= "wrap_content"
  8. android:layout_height= "wrap_content"
  9. android:layout_gravity= "bottom|center_horizontal"
  10. android:text= "点一次出一次"
  11. android:textSize= "12sp" />
  12. </com.dpdp.base_moudle.weight.FloatLikeView>

   
  1. // 设置爱心 文件
  2. layoutBinding.likeBtn01.setLikeDrawables(R.drawable.heart_01, R.drawable.heart_02, R.drawable.heart_03,
  3. R.drawable.heart_04, R.drawable.heart_05, R.drawable.heart_06);
  4. layoutBinding.praise01.setOnClickListener( new View.OnClickListener() {
  5. @Override
  6. public void onClick(View v) {
  7. // 爱心单个出现
  8. layoutBinding.likeBtn01.clickLikeView();
  9. }
  10. });
  11. layoutBinding.praise02.setOnClickListener( new View.OnClickListener() {
  12. @Override
  13. public void onClick(View v) {
  14. // 爱心动画自动播放
  15. layoutBinding.likeBtn01.autoPlayClickView( 20, true);
  16. //爱心播放动画停止
  17. //layoutBinding.likeBtn01.stopAutoPlay();
  18. }
  19. });

   
  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. //退出时销毁 防止动画还没播放完就退出出现问题
  5. layoutBinding.likeBtn01.release();
  6. }

java 完整代码放在最后

接下来是前面那个计时器 ,记录一段时间点赞的次数然后 发送给服务端 如果点击一下 发送一次,接口压力太大,所以考虑过段时间提交一次,于是乎就有了比较奇怪的交互。 

 

 

 

 实现逻辑:


   
  1. package com.dpdp.base_moudle;
  2. import android.animation.Animator;
  3. import android.animation.AnimatorListenerAdapter;
  4. import android.animation.AnimatorSet;
  5. import android.animation.ObjectAnimator;
  6. import android.content.Context;
  7. import android.content.res.TypedArray;
  8. import android.graphics.Canvas;
  9. import android.graphics.Color;
  10. import android.graphics.Paint;
  11. import android.graphics.RectF;
  12. import android.util.AttributeSet;
  13. import android.util.Log;
  14. import android.view.View;
  15. import android.view.animation.AccelerateInterpolator;
  16. import androidx.annotation.Keep;
  17. import androidx.annotation.Nullable;
  18. /**
  19. * Created by ldp.
  20. * <p>
  21. * Date: 2021-03-05
  22. * <p>
  23. * Summary: 点赞计数器 进度
  24. *
  25. * @see #firstClick() 可以用于 第一次 点击出现 不进行缩放 按照需要来
  26. * @see #release() 释放资源
  27. * @see #setLikeClickCallback(LikeClickCallback) 回调 点击次数
  28. */
  29. public class LiveClickLikeView extends View implements View.OnClickListener {
  30. private int mWidth; // View的宽度
  31. private int mHeight; // View的高度
  32. private Paint bgPaint; // 背景画笔
  33. private RectF rect; // 圆环内切圆矩形
  34. private Paint bgArcPaint; // 圆环画笔
  35. private Paint progressArcPaint; // 进度画笔
  36. private Paint textPaint; // 中间文字画笔
  37. private int angle; // 绘制角度
  38. private int defaultSize = 100; // 默认一个最大尺寸
  39. private int clickCounts = 0; // 点击数
  40. private AnimatorSet animatorSet; // 动画集合
  41. private int bgPaintColor; // 圆形 背景色
  42. private int progressColor; // 进度条的颜色
  43. private int textColor; // 文字的颜色
  44. private int textSize; // 文字的 大小
  45. private int progressWidth; // 进度条宽度
  46. private ObjectAnimator animator; // 第一次显示出来大小不改变只有进度更新的动画
  47. public LiveClickLikeView(Context context) {
  48. this(context, null);
  49. }
  50. public LiveClickLikeView(Context context, @Nullable AttributeSet attrs) {
  51. this(context, attrs, 0);
  52. }
  53. public LiveClickLikeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  54. super(context, attrs, defStyleAttr);
  55. if (attrs != null) {
  56. TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.LiveClickLikeView);
  57. bgPaintColor = attributes.getColor(R.styleable.LiveClickLikeView_like_click_bg_circle_color, Color.BLACK);
  58. progressColor = attributes.getColor(R.styleable.LiveClickLikeView_like_click_progress_color, Color.BLUE);
  59. textColor = attributes.getColor(R.styleable.LiveClickLikeView_like_click_text_color, Color.WHITE);
  60. textSize = attributes.getDimensionPixelSize(R.styleable.LiveClickLikeView_like_click_text_size, 20);
  61. progressWidth = attributes.getDimensionPixelSize(R.styleable.LiveClickLikeView_like_click_progress_width, 10);
  62. attributes.recycle();
  63. }
  64. initPaint();
  65. setOnClickListener( this);
  66. }
  67. private void initPaint() {
  68. bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  69. bgPaint.setColor(bgPaintColor);
  70. bgPaint.setStyle(Paint.Style.FILL);
  71. rect = new RectF();
  72. bgArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  73. bgArcPaint.setColor(progressColor);
  74. bgArcPaint.setStyle(Paint.Style.STROKE);
  75. bgArcPaint.setStrokeWidth(progressWidth);
  76. progressArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  77. progressArcPaint.setColor(bgPaintColor);
  78. progressArcPaint.setStyle(Paint.Style.STROKE);
  79. progressArcPaint.setStrokeWidth(progressWidth);
  80. textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  81. textPaint.setColor(textColor);
  82. textPaint.setStyle(Paint.Style.FILL);
  83. textPaint.setTextSize(textSize);
  84. }
  85. @Override
  86. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  87. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  88. // 测量宽高
  89. setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(heightMeasureSpec));
  90. // 获取宽高
  91. mHeight = getMeasuredHeight();
  92. mWidth = getMeasuredWidth();
  93. // 根据测量的宽高 计算 圆环进度条的 内切圆的矩形的宽高
  94. rect.left = progressWidth >> 1; // 等价于 progressWidth/2
  95. rect.right = mWidth - (progressWidth >> 1);
  96. rect.top = progressWidth >> 1;
  97. rect.bottom = mHeight - (progressWidth >> 1);
  98. }
  99. private int measureSize(int measureSpec) {
  100. int result = 0;
  101. int mode = MeasureSpec.getMode(measureSpec);
  102. int size = MeasureSpec.getSize(measureSpec);
  103. if (mode == MeasureSpec.EXACTLY) {
  104. // //当specMode = EXACTLY时,精确值模式,即当我们在布局文件中为View指定了具体的大小
  105. result = size;
  106. } else {
  107. result = defaultSize;
  108. if (mode == MeasureSpec.AT_MOST) {
  109. result = Math.min(defaultSize, size);
  110. }
  111. }
  112. return result;
  113. }
  114. @Override
  115. protected void onDraw(Canvas canvas) {
  116. super.onDraw(canvas);
  117. // 背景
  118. canvas.drawCircle(mWidth >> 1, mHeight >> 1, mWidth >> 1, bgPaint);
  119. // 圆环
  120. canvas.drawArc(rect, - 90, 360, false, bgArcPaint);
  121. // 叠加背景色一样的圆环 进度条消失动画的原理
  122. canvas.drawArc(rect, - 90, angle, false, progressArcPaint);
  123. // 测量文字宽高
  124. float textWidth = textPaint.measureText( "x " + clickCounts);
  125. Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
  126. float textHeight = fontMetrics.bottom - fontMetrics.top;
  127. //绘制文字 宽度超过 view宽度的 4/5,按0.8比例自动进行缩放
  128. while (textWidth > mWidth * 0.8f) {
  129. textSize = (( int) (textSize * 0.8f));
  130. textPaint.setTextSize(textSize);
  131. textWidth = textPaint.measureText( "x " + clickCounts);
  132. textHeight = fontMetrics.bottom - fontMetrics.top;
  133. }
  134. canvas.drawText( "x " + clickCounts, (mWidth - textWidth) / 2, (mHeight >> 1) + textHeight / 4, textPaint);
  135. }
  136. @Keep
  137. public void setAngle(int angle) {
  138. // 改变角度 不停地重绘 形成进度条效果
  139. this.angle = angle;
  140. invalidate();
  141. }
  142. // 区分用户频繁点击触发结束
  143. private boolean isUserCancel = false;
  144. @Override
  145. public void onClick(View v) {
  146. // 取消第一次的动画
  147. if (animator != null && animator.isRunning()) {
  148. animator.removeAllListeners();
  149. animator.cancel();
  150. }
  151. // 取消未完成的动画
  152. if (animatorSet != null && animatorSet.isRunning()) {
  153. isUserCancel = true;
  154. animatorSet.removeAllListeners();
  155. animatorSet.cancel();
  156. }
  157. //增加一次点击次数
  158. clickCounts++;
  159. // x缩放动画
  160. ObjectAnimator scaleX = ObjectAnimator.ofFloat( this, "scaleX", 1f, 1.5f, 1f);
  161. // y缩放动画
  162. ObjectAnimator scaleY = ObjectAnimator.ofFloat( this, "scaleY", 1f, 1.5f, 1f);
  163. // 角度 动画 改变角度 形成进度条动画
  164. ObjectAnimator angle = ObjectAnimator.ofInt( this, "angle", 0, 360);
  165. // 进度条动画持续5秒
  166. angle.setDuration( 5000);
  167. // 动画监听
  168. angle.addListener( new AnimatorListenerAdapter() {
  169. @Override
  170. public void onAnimationEnd(Animator animation) {
  171. super.onAnimationEnd(animation);
  172. // 如果是用户再次点击 取消上次动画,播放新的动画,上个动画结束不进行操作,
  173. // 只有动画自己执行完成才会回调 统计一次点赞次数
  174. if (isUserCancel) return;
  175. if (likeClickCallback != null) {
  176. // 回调5秒内的点击次数
  177. likeClickCallback.likeCountsCallback(clickCounts);
  178. // 重置 点击次数
  179. clickCounts = 0;
  180. }
  181. }
  182. });
  183. animatorSet = new AnimatorSet();
  184. // 组合动画
  185. animatorSet.playTogether(scaleX, scaleY, angle);
  186. animatorSet.setTarget( this);
  187. // 差值器
  188. animatorSet.setInterpolator( new AccelerateInterpolator());
  189. // 开始播放动画
  190. animatorSet.start();
  191. isUserCancel = false;
  192. }
  193. /**
  194. * 第一次点击的动画 只是进度条 不进行缩放
  195. */
  196. public void firstClick() {
  197. clickCounts++;
  198. animator = ObjectAnimator.ofInt( this, "angle", 0, 360);
  199. animator.setDuration( 5000);
  200. animator.addListener( new AnimatorListenerAdapter() {
  201. @Override
  202. public void onAnimationEnd(Animator animation) {
  203. super.onAnimationEnd(animation);
  204. if (isUserCancel) return;
  205. Log.e( "animation", "1 auto end " + clickCounts);
  206. if (likeClickCallback != null) {
  207. likeClickCallback.likeCountsCallback(clickCounts);
  208. clickCounts = 0;
  209. }
  210. }
  211. });
  212. animator.start();
  213. }
  214. /**
  215. * 结束动画释放资源
  216. */
  217. public void release() {
  218. if (animator != null) {
  219. animator.removeAllListeners();
  220. animator.cancel();
  221. animator = null;
  222. }
  223. if (animatorSet != null) {
  224. animatorSet.removeAllListeners();
  225. animatorSet.cancel();
  226. animatorSet = null;
  227. }
  228. }
  229. private LikeClickCallback likeClickCallback;
  230. public void setLikeClickCallback(LikeClickCallback likeClickCallback) {
  231. this.likeClickCallback = likeClickCallback;
  232. }
  233. public interface LikeClickCallback {
  234. void likeCountsCallback(int clickLikeCounts);
  235. }
  236. }

   
  1. <declare-styleable name="LiveClickLikeView">
  2. <!-- 进度条颜色-->
  3. <attr name="like_click_progress_color" format="color" />
  4. <!-- 进度条宽度-->
  5. <attr name="like_click_progress_width" format="dimension" />
  6. <!-- 圆形背景颜色-->
  7. <attr name="like_click_bg_circle_color" format="color" />
  8. <!-- 文字颜色-->
  9. <attr name="like_click_text_color" format="color" />
  10. <!-- 文字大小 会自动进行缩放 严格说是最大的文字大小-->
  11. <attr name="like_click_text_size" format="dimension" />
  12. </declare-styleable>

 

使用方法:


   
  1. <xxx.xxx(包名).LiveClickLikeView
  2. android:id= "@+id/like"
  3. android:layout_width= "60dp"
  4. android:layout_height= "60dp"
  5. android:layout_marginTop= "100dp"
  6. android:layout_marginBottom= "60dp"
  7. android:visibility= "gone"
  8. app:layout_constraintBottom_toTopOf= "@+id/praise_02"
  9. app:layout_constraintLeft_toLeftOf= "@+id/praise_02"
  10. app:layout_constraintRight_toRightOf= "@+id/praise_02"
  11. app:like_click_bg_circle_color= "@color/color_333333"
  12. app:like_click_progress_color= "@android:color/holo_blue_light"
  13. app:like_click_progress_width= "4dp"
  14. app:like_click_text_color= "@android:color/white"
  15. app:like_click_text_size= "20sp"
  16. tools:visibility= "visible" />

   
  1. layoutBinding.calculateNumBtn.setOnClickListener( new View.OnClickListener() {
  2. @Override
  3. public void onClick(View v) {
  4. // 第一次点击出现
  5. layoutBinding.like.setVisibility(View.VISIBLE);
  6. layoutBinding.like.firstClick();
  7. }
  8. });
  9. layoutBinding.like.setLikeClickCallback( new LiveClickLikeView.LikeClickCallback() {
  10. @Override
  11. public void likeCountsCallback(int clickLikeCounts) {
  12. // 点击回调
  13. ToastUtil.showMsg( "点击了 " + clickLikeCounts + " 次");
  14. }
  15. });

   
  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. // 及时结束动画
  5. layoutBinding.like.release();
  6. }

完整代码在最后

完整代码:布局解析用了 ViewBinding,提一句: Kotlin不用写finviewbyid的方式已经过时了,居然已经过时了!!! Google 推荐使用ViewBing !!!


   
  1. package com.example.administrator.words;
  2. import android.os.Bundle;
  3. import android.view.LayoutInflater;
  4. import android.view.View;
  5. import androidx.annotation.Nullable;
  6. import com.dpdp.base_moudle.LiveClickLikeView;
  7. import com.dpdp.base_moudle.base.ui.BaseActivity;
  8. import com.dpdp.base_moudle.utils.ToastUtil;
  9. import com.example.administrator.words.databinding.ActivityGoodTestLayoutBinding;
  10. /**
  11. * Created by ldp.
  12. * <p>
  13. * Date: 2021-02-23
  14. * <p>
  15. * Summary:
  16. * <p>
  17. */
  18. public class TestGoodActivity extends BaseActivity {
  19. private ActivityGoodTestLayoutBinding layoutBinding;
  20. @Override
  21. protected void onCreate(@Nullable Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. layoutBinding = ActivityGoodTestLayoutBinding.inflate(LayoutInflater.from( this));
  24. setContentView(layoutBinding.getRoot());
  25. // 设置爱心 文件
  26. layoutBinding.likeBtn01.setLikeDrawables(R.drawable.heart_01, R.drawable.heart_02, R.drawable.heart_03,
  27. R.drawable.heart_04, R.drawable.heart_05, R.drawable.heart_06);
  28. layoutBinding.praise01.setOnClickListener( new View.OnClickListener() {
  29. @Override
  30. public void onClick(View v) {
  31. // 爱心单个出现
  32. layoutBinding.likeBtn01.clickLikeView();
  33. }
  34. });
  35. layoutBinding.praise02.setOnClickListener( new View.OnClickListener() {
  36. @Override
  37. public void onClick(View v) {
  38. // 爱心动画自动播放
  39. layoutBinding.likeBtn01.autoPlayClickView( 20, true);
  40. //爱心播放动画停止
  41. //layoutBinding.likeBtn01.stopAutoPlay();
  42. }
  43. });
  44. layoutBinding.calculateNumBtn.setOnClickListener( new View.OnClickListener() {
  45. @Override
  46. public void onClick(View v) {
  47. // 第一次点击出现
  48. layoutBinding.like.setVisibility(View.VISIBLE);
  49. layoutBinding.like.firstClick();
  50. }
  51. });
  52. layoutBinding.like.setLikeClickCallback( new LiveClickLikeView.LikeClickCallback() {
  53. @Override
  54. public void likeCountsCallback(int clickLikeCounts) {
  55. // 点击回调
  56. ToastUtil.showMsg( "点击了 " + clickLikeCounts + " 次");
  57. }
  58. });
  59. }
  60. @Override
  61. protected void onDestroy() {
  62. super.onDestroy();
  63. layoutBinding.likeBtn01.release();
  64. // 及时结束动画
  65. layoutBinding.like.release();
  66. }
  67. }

 


   
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app= "http://schemas.android.com/apk/res-auto"
  4. xmlns:tools= "http://schemas.android.com/tools"
  5. android:layout_width= "match_parent"
  6. android:layout_height= "match_parent">
  7. <com.dpdp.base_moudle.weight.FloatLikeView
  8. android:id= "@+id/like_btn_01"
  9. android:layout_width= "100dp"
  10. android:layout_height= "400dp"
  11. app:layout_constraintBottom_toBottomOf= "parent"
  12. app:layout_constraintLeft_toLeftOf= "parent"
  13. app:layout_constraintRight_toLeftOf= "@+id/praise_02">
  14. <Button
  15. android:id= "@+id/praise_01"
  16. android:layout_width= "wrap_content"
  17. android:layout_height= "wrap_content"
  18. android:layout_gravity= "bottom|center_horizontal"
  19. android:text= "点一次出一次"
  20. android:textSize= "12sp" />
  21. </com.dpdp.base_moudle.weight.FloatLikeView>
  22. <Button
  23. android:id= "@+id/praise_02"
  24. android:layout_width= "wrap_content"
  25. android:layout_height= "wrap_content"
  26. android:layout_marginLeft= "6dp"
  27. android:text= "自动播放点赞"
  28. android:textSize= "12sp"
  29. app:layout_constraintBottom_toBottomOf= "parent"
  30. app:layout_constraintLeft_toRightOf= "@+id/like_btn_01"
  31. app:layout_constraintRight_toRightOf= "parent" />
  32. <com.dpdp.base_moudle.LiveClickLikeView
  33. android:id= "@+id/like"
  34. android:layout_width= "60dp"
  35. android:layout_height= "60dp"
  36. android:layout_marginTop= "100dp"
  37. android:layout_marginBottom= "60dp"
  38. android:visibility= "gone"
  39. app:layout_constraintBottom_toTopOf= "@+id/praise_02"
  40. app:layout_constraintLeft_toLeftOf= "@+id/praise_02"
  41. app:layout_constraintRight_toRightOf= "@+id/praise_02"
  42. app:like_click_bg_circle_color= "@color/color_333333"
  43. app:like_click_progress_color= "@android:color/holo_blue_light"
  44. app:like_click_progress_width= "4dp"
  45. app:like_click_text_color= "@android:color/white"
  46. app:like_click_text_size= "20sp"
  47. tools:visibility= "visible" />
  48. <Button
  49. android:id= "@+id/calculate_num_btn"
  50. android:layout_width= "wrap_content"
  51. android:layout_height= "wrap_content"
  52. android:layout_marginBottom= "200dp"
  53. android:text= "计数器"
  54. app:layout_constraintBottom_toTopOf= "@+id/praise_02"
  55. app:layout_constraintLeft_toLeftOf= "@+id/praise_02"
  56. app:layout_constraintRight_toRightOf= "@+id/praise_02" />
  57. </androidx.constraintlayout.widget.ConstraintLayout>

 

感谢您的拜读! 源代码地址:

1、github源码地址,谢谢您的支持!

2、CSDN免费下载地址

部分源码用kotlin进行修正编写,博客里面没有做修改,大同小异,加油,各位!

编辑于-------2021年3月15日14:52:15 

 

 


转载:https://blog.csdn.net/Mr_Liangxiaobai/article/details/114531518
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场