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

 更新时间:2018年03月28日 16:54:24   转载 作者:android_小路  
这篇文章主要为大家详细介绍了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

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

相关文章

最新评论