admin 管理员组

文章数量: 887007

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

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

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

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

2.定义飘心默认值

2.1 dimens.xml

50.0dp

50.0dp

25.0dp

400.0dp

350.0dp

30.0dp

27.3dp

32.5dp

2.2 integers.xml

6

3000

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 wf;

public HeartHandler(HeartLayout layout) {

wf = new WeakReference(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

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@color/grey"

android:alpha="0.5">

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"

/>

android:id="@+id/heart_layout"

android:layout_width="100dp"

android:layout_height="wrap_content"

android:layout_alignParentRight="true"

android:layout_alignParentBottom="true"/>

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.参看资料

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

本文标签: android点赞头像动画 android实现直播点赞飘心动画效果