使用surfaceview实现直播中的点赞效果
2016-09-03 17:17
393 查看
转载请注明出处 http://blog.csdn.net/u011453163/article/details/52424328
直播功能现在已经是一个很热门的功能了,很多应用都会涉及到直播模块,比如 花椒 NOW 还有辣妈帮。。等,最近因为项目需要也加入了直播功能。直播中有一个点赞的效果 ,今天我也按照自己的思路实现了一个这样的点赞功能,效果如下:
简单描述一下效果
1.产生一颗心
2.由下至上的曲线运动
3.开头有一段放大效果
4.透明度渐变效果
实现以上效果涉及到的知识点
曲线轨迹 :属性动画+贝赛尔曲线算法
由于点赞效果在直播中的持续时间是比较长的
所以这里使用的是surfaceview 可以在工作线程中绘制ui
ui绘制:surfaceview
该效果的轨迹原型图
轨迹坐标点是通过属性动画 TypeEvaluator 生成的
接下来分享一下代码的实现思路
由两个类构成的 Zanbean ZanView
Zanbean
用来记录心的轨迹 和 绘制的工作
这里简单介绍一下init方法
在创建对象的时候 属性动画会直接启动。更符合场景,相当每个心丢进画面就会自动跑起来,每个心都是独立的个体
然后是ZanView 画面绘制和呈现的主体
以上是ZanView的所有代码 重要的地方都做了注释 还是比较好懂的吧
为了验证贝赛尔曲线
只要将这句话注释就能看到每一帧的轨迹
以上就是关于点赞 心动的效果 有什么问题欢迎指出
直播功能现在已经是一个很热门的功能了,很多应用都会涉及到直播模块,比如 花椒 NOW 还有辣妈帮。。等,最近因为项目需要也加入了直播功能。直播中有一个点赞的效果 ,今天我也按照自己的思路实现了一个这样的点赞功能,效果如下:
简单描述一下效果
1.产生一颗心
2.由下至上的曲线运动
3.开头有一段放大效果
4.透明度渐变效果
实现以上效果涉及到的知识点
曲线轨迹 :属性动画+贝赛尔曲线算法
由于点赞效果在直播中的持续时间是比较长的
所以这里使用的是surfaceview 可以在工作线程中绘制ui
ui绘制:surfaceview
该效果的轨迹原型图
轨迹坐标点是通过属性动画 TypeEvaluator 生成的
private class BezierEvaluator implements TypeEvaluator<Point> { private Point centerPoint; public BezierEvaluator(Point centerPoint) { this.centerPoint = centerPoint; } @Override public Point evaluate(float t, Point startValue, Point endValue) { int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * centerPoint.x + t * t * endValue.x); int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * centerPoint.y + t * t * endValue.y); return new Point(x, y); } }
接下来分享一下代码的实现思路
由两个类构成的 Zanbean ZanView
public class ZanBean { /**心的当前坐标*/ public Point point; /**移动动画*/ private ValueAnimator moveAnim; /**放大动画*/ private ValueAnimator zoomAnim; /**透明度*/ public int alpha=255;// /**心图*/ private Bitmap bitmap; /**绘制bitmap的矩阵 用来做缩放和移动的*/ private Matrix matrix = new Matrix(); /**缩放系数*/ private float sf=0; /**产生随机数*/ private Random random; public boolean isEnd=false;//是否结束 public ZanBean(Context context,int resId,ZanView zanView) { random=new Random(); bitmap= BitmapFactory.decodeResource(context.getResources(),resId); init(new Point(zanView.getWidth() / 2, zanView.getHeight()), new Point((random.nextInt(zanView.getWidth())), 0)); } public ZanBean(Context context,Bitmap bitmap,ZanView zanView) { random=new Random(); this.bitmap= bitmap; init(new Point(zanView.getWidth() / 2, zanView.getHeight()), new Point((random.nextInt(zanView.getWidth())), 0)); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void init(final Point startPoint, Point endPoint){ moveAnim =ValueAnimator.ofObject(new BezierEvaluator(new Point(random.nextInt(startPoint.x*2),Math.abs(endPoint.y-startPoint.y)/2)),startPoint,endPoint); moveAnim.setDuration(2500); moveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { point= (Point) animation.getAnimatedValue(); alpha= (int) ((float)point.y/(float)startPoint.y*255); } }); moveAnim.start(); zoomAnim =ValueAnimator.ofFloat(0,1f).setDuration(700); zoomAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Float f= (Float) animation.getAnimatedValue(); sf=f.floatValue(); } }); zoomAnim.start(); } public void pause(){ if(moveAnim !=null&& moveAnim.isRunning()){ moveAnim.pause(); } if(zoomAnim !=null&& zoomAnim.isRunning()){ zoomAnim.pause(); } } public void resume(){ if(moveAnim !=null&& moveAnim.isPaused()){ moveAnim.resume(); } if(zoomAnim !=null&& zoomAnim.isPaused()){ zoomAnim.resume(); } } /**主要绘制函数*/ public void draw(Canvas canvas, Paint p){ if(bitmap!=null&&alpha>0) { p.setAlpha(alpha); matrix.setScale(sf,sf,bitmap.getWidth()/2,bitmap.getHeight()/2); matrix.postTranslate(point.x-bitmap.getWidth()/2,point.y-bitmap.getHeight()/2); canvas.drawBitmap(bitmap, matrix, p); }else { isEnd=true; } } /** * 二次贝塞尔曲线 */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) private class BezierEvaluator implements TypeEvaluator<Point> { private Point centerPoint; public BezierEvaluator(Point centerPoint) { this.centerPoint = centerPoint; } @Override public Point evaluate(float t, Point startValue, Point endValue) { int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * centerPoint.x + t * t * endValue.x); int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * centerPoint.y + t * t * endValue.y); return new Point(x, y); } } }
Zanbean
用来记录心的轨迹 和 绘制的工作
这里简单介绍一下init方法
@TargetApi(Build.VERSION_CODES.HONEYCOMB) private void init(final Point startPoint, Point endPoint){ moveAnim =ValueAnimator.ofObject(new BezierEvaluator(new Point(random.nextInt(startPoint.x*2),Math.abs(endPoint.y-startPoint.y)/2)),startPoint,endPoint); moveAnim.setDuration(2500); moveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { point= (Point) animation.getAnimatedValue(); alpha= (int) ((float)point.y/(float)startPoint.y*255); } }); moveAnim.start(); zoomAnim =ValueAnimator.ofFloat(0,1f).setDuration(700); zoomAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Float f= (Float) animation.getAnimatedValue(); sf=f.floatValue(); } }); zoomAnim.start(); }
在创建对象的时候 属性动画会直接启动。更符合场景,相当每个心丢进画面就会自动跑起来,每个心都是独立的个体
然后是ZanView 画面绘制和呈现的主体
public class ZanView extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder surfaceHolder; /**心的个数*/ private ArrayList<ZanBean> zanBeen = new ArrayList<>(); private Paint p; /**负责绘制的工作线程*/ private DrawThread drawThread; public ZanView(Context context) { this(context, null); } public ZanView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ZanView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.setZOrderOnTop(true); /**设置画布 背景透明*/ this.getHolder().setFormat(PixelFormat.TRANSLUCENT); surfaceHolder = getHolder(); surfaceHolder.addCallback(this); p = new Paint(); p.setAntiAlias(true); drawThread = new DrawThread(); } /**点赞动作 添加心的函数 控制画面最大心的个数*/ public void addZanXin(ZanBean zanBean){ zanBeen.add(zanBean); if(zanBeen.size()>40){ zanBeen.remove(0); } start(); } @Override public void surfaceCreated(SurfaceHolder holder) { if(drawThread==null){ drawThread=new DrawThread(); } drawThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { if(drawThread!=null){ drawThread.isRun = false; drawThread=null; } } class DrawThread extends Thread { boolean isRun = true; @Override public void run() { super.run(); /**绘制的线程 死循环 不断的跑动*/ while (isRun) { Canvas canvas = null; try { synchronized (surfaceHolder) { canvas = surfaceHolder.lockCanvas(); /**清除画面*/ canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); boolean isEnd=true; /**对所有心进行遍历绘制*/ for (int i = 0; i < zanBeen.size(); i++) { isEnd=zanBeen.get(i).isEnd; zanBeen.get(i).draw(canvas, p); } /**这里做一个性能优化的动作,由于线程是死循环的 在没有心需要的绘制的时候会结束线程*/ if(isEnd){ isRun=false; drawThread=null; } } } catch (Exception e) { e.printStackTrace(); } finally { if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } } try { /**用于控制绘制帧率*/ Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } }} } public void stop(){ if(drawThread!=null){ for (int i = 0; i < zanBeen.size(); i++) { zanBeen.get(i).pause(); } drawThread.isRun=false; drawThread=null; } } public void start(){ if(drawThread==null){ for (int i = 0; i < zanBeen.size(); i++) { zanBeen.get(i).resume(); } drawThread=new DrawThread(); drawThread.start();} } }
以上是ZanView的所有代码 重要的地方都做了注释 还是比较好懂的吧
为了验证贝赛尔曲线
只要将这句话注释就能看到每一帧的轨迹
/**清除画面*/ canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
以上就是关于点赞 心动的效果 有什么问题欢迎指出
相关文章推荐
- Android使用SurfaceView实现墨迹天气的风车效果
- android 使用SurfaceView实现小球高处落下并弹起的效果
- Android使用SurfaceView实现墨迹天气的风车效果
- android 使用SurfaceView实现小球高处落下并弹起的效果
- Android使用SurfaceView实现墨迹天气的风车效果
- SurfaceView实现点赞效果
- 使用三阶贝塞尔曲线实现直播中点赞效果
- Android自定义View——贝塞尔曲线实现直播点赞效果
- Android使用SurfaceView实现墨迹天气的风车效果
- 使用viewGroupe实现左右拖到的效果
- Android中如何使用ViewPager实现类似laucher左右拖动效果
- Android使用ViewPager实现左右滑动效果
- Android-使用ViewPager实现高仿launcher拖动效果(转)
- Qt使用QGraphicsView实现滑动窗体效果
- Android中如何使用ViewPager实现类似laucher左右拖动效果
- Qt使用QGraphicsView实现滑动窗体效果
- Android中使用ViewPager和PagerTabStrip实现页面滑动效果
- Android中如何使用ViewPager实现类似laucher左右拖动效果
- 使用ViewPager实现高仿Launcher的拖动效果
- Qt使用QGraphicsView实现滑动窗体效果