首页 > 系统 > Android > 正文

android实现直播点赞飘心动画效果

2019-10-22 18:13:33
字体:
来源:转载
供稿:网友

前段时间在写直播的时候,需要观众在看直播的时候点赞的效果,在此参照了腾讯大神写的点赞(飘心动画效果)。下面是效果图:

android,直播,点赞,动画

1.自定义飘心动画的属性

在attrs.xml 中增加自定义的属性

<!-- 飘心动画自定义的属性 --> <declare-styleable name="HeartLayout">  <attr name="initX" format="dimension"/>  <attr name="initY" format="dimension"/>  <attr name="xRand" format="dimension"/>  <attr name="animLengthRand" format="dimension"/>  <attr name="xPointFactor" format="dimension"/>  <attr name="animLength" format="dimension"/>  <attr name="heart_width" format="dimension"/>  <attr name="heart_height" format="dimension"/>  <attr name="bezierFactor" format="integer"/>  <attr name="anim_duration" format="integer"/> </declare-styleable>

2.定义飘心默认值

2.1 dimens.xml

<!-- 飘星 --> <dimen name="heart_anim_bezier_x_rand">50.0dp</dimen> <dimen name="heart_anim_init_x">50.0dp</dimen> <dimen name="heart_anim_init_y">25.0dp</dimen> <dimen name="heart_anim_length">400.0dp</dimen> <dimen name="heart_anim_length_rand">350.0dp</dimen> <dimen name="heart_anim_x_point_factor">30.0dp</dimen> <dimen name="heart_size_height">27.3dp</dimen> <dimen name="heart_size_width">32.5dp</dimen>

2.2 integers.xml

<?xml version="1.0" encoding="utf-8"?><resources> <integer name="heart_anim_bezier_factor">6</integer> <integer name="anim_duration">3000</integer></resources>

3.定义飘心动画控制器

3.1 AbstractPathAnimator.java

public abstract class AbstractPathAnimator { private final Random mRandom; protected final Config mConfig; public AbstractPathAnimator(Config config) {  mConfig = config;  mRandom = new Random(); } public float randomRotation() {  return mRandom.nextFloat() * 28.6F - 14.3F; } public Path createPath(AtomicInteger counter, View view, int factor) {  Random r = mRandom;  int x = r.nextInt(mConfig.xRand);  int x2 = r.nextInt(mConfig.xRand);  int y = view.getHeight() - mConfig.initY;  int y2 = counter.intValue() * 15 + mConfig.animLength * factor + r.nextInt(mConfig.animLengthRand);  factor = y2 / mConfig.bezierFactor;  x = mConfig.xPointFactor + x;  x2 = mConfig.xPointFactor + x2;  int y3 = y - y2;  y2 = y - y2 / 2;  Path p = new Path();  p.moveTo(mConfig.initX, y);  p.cubicTo(mConfig.initX, y - factor, x, y2 + factor, x, y2);  p.moveTo(x, y2);  p.cubicTo(x, y2 - factor, x2, y3 + factor, x2, y3);  return p; } public abstract void start(View child, ViewGroup parent); public static class Config {  public int initX;  public int initY;  public int xRand;  public int animLengthRand;  public int bezierFactor;  public int xPointFactor;  public int animLength;  public int heartWidth;  public int heartHeight;  public int animDuration;  static public Config fromTypeArray(TypedArray typedArray, float x, float y, int pointx, int heartWidth, int heartHeight) {   Config config = new Config();   Resources res = typedArray.getResources();   config.initX = (int) typedArray.getDimension(R.styleable.HeartLayout_initX,     x);   config.initY = (int) typedArray.getDimension(R.styleable.HeartLayout_initY,     y);   config.xRand = (int) typedArray.getDimension(R.styleable.HeartLayout_xRand,     res.getDimensionPixelOffset(R.dimen.heart_anim_bezier_x_rand));   config.animLength = (int) typedArray.getDimension(R.styleable.HeartLayout_animLength,     res.getDimensionPixelOffset(R.dimen.heart_anim_length));//动画长度   config.animLengthRand = (int) typedArray.getDimension(R.styleable.HeartLayout_animLengthRand,     res.getDimensionPixelOffset(R.dimen.heart_anim_length_rand));   config.bezierFactor = typedArray.getInteger(R.styleable.HeartLayout_bezierFactor,     res.getInteger(R.integer.heart_anim_bezier_factor));   config.xPointFactor = pointx;//   config.heartWidth = (int) typedArray.getDimension(R.styleable.HeartLayout_heart_width,//     res.getDimensionPixelOffset(R.dimen.heart_size_width));//动画图片宽度//   config.heartHeight = (int) typedArray.getDimension(R.styleable.HeartLayout_heart_height,//     res.getDimensionPixelOffset(R.dimen.heart_size_height));//动画图片高度   config.heartWidth = heartWidth;   config.heartHeight = heartHeight;   config.animDuration = typedArray.getInteger(R.styleable.HeartLayout_anim_duration,     res.getInteger(R.integer.anim_duration));//持续期   return config;  } }}

3.2 PathAnimator.java

/** * 飘心路径动画器 */public class PathAnimator extends AbstractPathAnimator { private final AtomicInteger mCounter = new AtomicInteger(0); private Handler mHandler; public PathAnimator(Config config) {  super(config);  mHandler = new Handler(Looper.getMainLooper()); } @Override public void start(final View child, final ViewGroup parent) {  parent.addView(child, new ViewGroup.LayoutParams(mConfig.heartWidth, mConfig.heartHeight));  FloatAnimation anim = new FloatAnimation(createPath(mCounter, parent, 2), randomRotation(), parent, child);  anim.setDuration(mConfig.animDuration);  anim.setInterpolator(new LinearInterpolator());  anim.setAnimationListener(new Animation.AnimationListener() {   @Override   public void onAnimationEnd(Animation animation) {    mHandler.post(new Runnable() {     @Override     public void run() {      parent.removeView(child);     }    });    mCounter.decrementAndGet();   }   @Override   public void onAnimationRepeat(Animation animation) {   }   @Override   public void onAnimationStart(Animation animation) {    mCounter.incrementAndGet();   }  });  anim.setInterpolator(new LinearInterpolator());  child.startAnimation(anim); } static class FloatAnimation extends Animation {  private PathMeasure mPm;  private View mView;  private float mDistance;  private float mRotation;  public FloatAnimation(Path path, float rotation, View parent, View child) {   mPm = new PathMeasure(path, false);   mDistance = mPm.getLength();   mView = child;   mRotation = rotation;   parent.setLayerType(View.LAYER_TYPE_HARDWARE, null);  }  @Override  protected void applyTransformation(float factor, Transformation transformation) {   Matrix matrix = transformation.getMatrix();   mPm.getMatrix(mDistance * factor, matrix, PathMeasure.POSITION_MATRIX_FLAG);   mView.setRotation(mRotation * factor);   float scale = 1F;   if (3000.0F * factor < 200.0F) {    scale = scale(factor, 0.0D, 0.06666667014360428D, 0.20000000298023224D, 1.100000023841858D);   } else if (3000.0F * factor < 300.0F) {    scale = scale(factor, 0.06666667014360428D, 0.10000000149011612D, 1.100000023841858D, 1.0D);   }   mView.setScaleX(scale);   mView.setScaleY(scale);   transformation.setAlpha(1.0F - factor);  } } private static float scale(double a, double b, double c, double d, double e) {  return (float) ((a - b) / (c - b) * (e - d) + d); }}

4.定义飘心界面

4.1 HeartView.java

/** * 飘心动画的界面 */public class HeartView extends ImageView{ //绘制的时候抗锯齿 private static final Paint sPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); private static final Canvas sCanvas = new Canvas(); private int mHeartResId = R.drawable.heart0; private int mHeartBorderResId = R.drawable.heart1; private static Bitmap sHeart; private static Bitmap sHeartBorder; public HeartView(Context context) {  super(context); } public HeartView(Context context, AttributeSet attrs) {  super(context, attrs); } public HeartView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr); } public void setDrawable(int resourceId){  Bitmap heart = BitmapFactory.decodeResource(getResources(), resourceId);  // Sets a drawable as the content of this ImageView.  setImageDrawable(new BitmapDrawable(getResources(),heart)); } public void setColor(int color) {  Bitmap heart = createHeart(color);  setImageDrawable(new BitmapDrawable(getResources(), heart)); } public void setColorAndDrawables(int color, int heartResId, int heartBorderResId) {  if (heartResId != mHeartResId) {   sHeart = null;  }  if (heartBorderResId != mHeartBorderResId) {   sHeartBorder = null;  }  mHeartResId = heartResId;  mHeartBorderResId = heartBorderResId;  setColor(color); } public Bitmap createHeart(int color) {  if (sHeart == null) {   sHeart = BitmapFactory.decodeResource(getResources(), mHeartResId);  }  if (sHeartBorder == null) {   sHeartBorder = BitmapFactory.decodeResource(getResources(), mHeartBorderResId);  }  Bitmap heart = sHeart;  Bitmap heartBorder = sHeartBorder;  Bitmap bm = createBitmapSafely(heartBorder.getWidth(), heartBorder.getHeight());  if (bm == null) {   return null;  }  Canvas canvas = sCanvas;  canvas.setBitmap(bm);  Paint p = sPaint;  canvas.drawBitmap(heartBorder, 0, 0, p);  p.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));  float dx = (heartBorder.getWidth() - heart.getWidth()) / 2f;  float dy = (heartBorder.getHeight() - heart.getHeight()) / 2f;  canvas.drawBitmap(heart, dx, dy, p);  p.setColorFilter(null);  canvas.setBitmap(null);  return bm; } private static Bitmap createBitmapSafely(int width, int height) {  try {   return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);  } catch (OutOfMemoryError error) {   error.printStackTrace();  }  return null; }}

4.2 飘心动画路径布局

HeartLayout.java

 

/** * 飘心动画路径 */public class HeartLayout extends RelativeLayout implements View.OnClickListener { private AbstractPathAnimator mAnimator; private AttributeSet attrs = null; private int defStyleAttr = 0; private OnHearLayoutListener onHearLayoutListener; private static HeartHandler heartHandler; private static HeartThread heartThread; public void setOnHearLayoutListener(OnHearLayoutListener onHearLayoutListener) {  this.onHearLayoutListener = onHearLayoutListener; } public interface OnHearLayoutListener {  boolean onAddFavor(); } public HeartLayout(Context context) {  super(context);  findViewById(context); } public HeartLayout(Context context, AttributeSet attrs) {  super(context, attrs);  this.attrs = attrs;  findViewById(context); } public HeartLayout(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  this.attrs = attrs;  this.defStyleAttr = defStyleAttr;  findViewById(context); } private Bitmap bitmap; private void findViewById(Context context) {  LayoutInflater.from(context).inflate(R.layout.ly_periscope, this);  bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_like);  dHeight = bitmap.getWidth()/2;  dWidth = bitmap.getHeight()/2;  textHight = sp2px(getContext(), 20) + dHeight / 2;  pointx = dWidth;//随机上浮方向的x坐标  bitmap.recycle(); } private int mHeight; private int mWidth; private int textHight; private int dHeight; private int dWidth; private int initX; private int pointx; public static int sp2px(Context context, float spValue) {  final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;  return (int) (spValue * fontScale + 0.5f); } public class HeartHandler extends Handler {  public final static int MSG_SHOW = 1;  WeakReference<HeartLayout> wf;  public HeartHandler(HeartLayout layout) {   wf = new WeakReference<HeartLayout>(layout);  }  @Override  public void handleMessage(Message msg) {   super.handleMessage(msg);   HeartLayout layout = wf.get();   if (layout == null) return;   switch (msg.what) {    case MSG_SHOW:     addFavor();     break;   }  } } public class HeartThread implements Runnable {  private long time = 0;  private int allSize = 0;  public void addTask(long time, int size) {   this.time = time;   allSize += size;  }  public void clean() {   allSize = 0;  }  @Override  public void run() {   if (heartHandler == null) return;   if (allSize > 0) {    heartHandler.sendEmptyMessage(HeartHandler.MSG_SHOW);    allSize--;   }   postDelayed(this, time);  } } private void init(AttributeSet attrs, int defStyleAttr) {  final TypedArray a = getContext().obtainStyledAttributes(    attrs, R.styleable.HeartLayout, defStyleAttr, 0);  if (pointx <= initX && pointx >= 0) {   pointx -= 10;  } else if (pointx >= -initX && pointx <= 0) {   pointx += 10;  } else pointx = initX;  mAnimator = new PathAnimator(AbstractPathAnimator.Config.fromTypeArray(a, initX, textHight, pointx, dWidth, dHeight));  a.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  super.onMeasure(widthMeasureSpec, heightMeasureSpec);  //获取本身的宽高 这里要注意,测量之后才有宽高  mWidth = getMeasuredWidth();  mHeight = getMeasuredHeight();  initX = mWidth / 2 - dWidth / 2; } public AbstractPathAnimator getAnimator() {  return mAnimator; } public void setAnimator(AbstractPathAnimator animator) {  clearAnimation();  mAnimator = animator; } public void clearAnimation() {  for (int i = 0; i < getChildCount(); i++) {   getChildAt(i).clearAnimation();  }  removeAllViews(); } private static int[] drawableIds = new int[]{R.drawable.heart0, R.drawable.heart1, R.drawable.heart2, R.drawable.heart3, R.drawable.heart4, R.drawable.heart5, R.drawable.heart6, R.drawable.heart7, R.drawable.heart8,}; private Random random = new Random(); public void addFavor() {  HeartView heartView = new HeartView(getContext());  heartView.setDrawable(drawableIds[random.nextInt(8)]);  init(attrs, defStyleAttr);  mAnimator.start(heartView, this); } private long nowTime, lastTime; final static int[] sizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999,   99999999, 999999999, Integer.MAX_VALUE}; public static int sizeOfInt(int x) {  for (int i = 0; ; i++)   if (x <= sizeTable[i])    return i + 1; } public void addFavor(int size) {  switch (sizeOfInt(size)) {   case 1:    size = size % 10;    break;   default:    size = size % 100;  }  if (size == 0) return;  nowTime = System.currentTimeMillis();  long time = nowTime - lastTime;  if (lastTime == 0)   time = 2 * 1000;//第一次分为2秒显示完  time = time / (size + 15);  if (heartThread == null) {   heartThread = new HeartThread();  }  if (heartHandler == null) {   heartHandler = new HeartHandler(this);   heartHandler.post(heartThread);  }  heartThread.addTask(time, size);  lastTime = nowTime; } public void addHeart(int color) {  HeartView heartView = new HeartView(getContext());  heartView.setColor(color);  init(attrs, defStyleAttr);  mAnimator.start(heartView, this); } public void addHeart(int color, int heartResId, int heartBorderResId) {  HeartView heartView = new HeartView(getContext());  heartView.setColorAndDrawables(color, heartResId, heartBorderResId);  init(attrs, defStyleAttr);  mAnimator.start(heartView, this); } @Override public void onClick(View v) {  int i = v.getId();  if (i == R.id.img) {   if (onHearLayoutListener != null) {    boolean isAdd = onHearLayoutListener.onAddFavor();    if (isAdd) addFavor();   }  } } public void clean() {  if (heartThread != null) {   heartThread.clean();  } } public void release() {  if (heartHandler != null) {   heartHandler.removeCallbacks(heartThread);   heartThread = null;   heartHandler = null;  } }}

5.飘心动画的使用

5.1 activity_heart_animal.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/grey"    android:alpha="0.5"> <TextView   android:id="@+id/member_send_good"   android:layout_width="40dp"   android:layout_height="40dp"   android:layout_gravity="center"   android:layout_alignParentBottom="true"   android:layout_alignParentRight="true"   android:layout_marginRight="30dp"   android:layout_marginBottom="10dp"   android:background="@drawable/live_like_icon"   /> <!-- 飘心的路径 --> <com.myapplication2.app.newsdemo.view.heartview.HeartLayout   android:id="@+id/heart_layout"   android:layout_width="100dp"   android:layout_height="wrap_content"   android:layout_alignParentRight="true"   android:layout_alignParentBottom="true"/></RelativeLayout>

5.2 activity 中的使用

heartLayout = (HeartLayout)findViewById(R.id.heart_layout);  findViewById(R.id.member_send_good).setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {    heartLayout.addFavor();   }  });

heartLayout.addFavor(); 就是触发飘心动画效果的关键代码

6.参看资料

https://github.com/zhaoyang21cn/Android_Suixinbo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表