您的位置:首页 > 其它

使用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 生成的

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




以上就是关于点赞 心动的效果 有什么问题欢迎指出
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  动画 点赞 心动效果