前段时间在写直播的时候,需要观众在看直播的时候点赞的效果,在此参照了腾讯大神写的点赞(飘心动画效果)。下面是效果图:
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,y2); p.moveto(x,y2); p.cubicTo(x,y2 - factor,x2,y3 + factor,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(),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.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,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,attrs); this.attrs = attrs; findVIEwByID(context); } public HeartLayout(Context context,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,this); } public voID addHeart(int color,int heartborderResID) { HeartVIEw heartVIEw = new HeartVIEw(getContext()); heartVIEw.setcolorAndDrawables(color,heartResID,heartborderResID); init(attrs,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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
您可能感兴趣的文章:Android控件实现直播App特效之点赞飘心动画Android仿直播特效之点赞飘心效果Android贝塞尔曲线实现直播点赞效果Android高级UI特效仿直播点赞动画效果 总结以上是内存溢出为你收集整理的android实现直播点赞飘心动画效果全部内容,希望文章能够帮你解决android实现直播点赞飘心动画效果所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)