編輯:關於Android編程
直播功能現在已經是一個很熱門的功能了,很多應用都會涉及到直播模塊,比如 花椒 NOW 還有辣媽幫。。等,最近因為項目需要也加入了直播功能。直播中有一個點贊的效果 ,今天我也按照自己的思路實現了一個這樣的點贊功能,效果如下:

簡單描述一下效果
1.產生一顆心
2.由下至上的曲線運動
3.開頭有一段放大效果
4.透明度漸變效果<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPsq1z9bS1MnP0Ke5+8nmvLC1vbXE1qrKtrXjPGJyIC8+DQrH+s/fuey8oyCjusr00NS2r7uto6uxtMj8tvvH+s/fy+O3qDxiciAvPg0K08nT2rXj1N7Qp7n71NrWsbKl1tC1xLPW0PjKsbzkysexyL3Ps6S1xDxiciAvPg0Ky/nS1NXiwO/KudPDtcTKx3N1cmZhY2V2aWV3IL/J0tTU2rmk1/fP37PM1tC75tbGdWk8YnIgLz4NCnVpu+bWxqO6c3VyZmFjZXZpZXc8L3A+DQo8cD64w9Cnufu1xLnsvKPUrdDNzbw8YnIgLz4NCjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160905/20160905095858441.png" title="\" />
軌跡坐標點是通過屬性動畫 TypeEvaluator 生成的
private class BezierEvaluator implements TypeEvaluator{ 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 {
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 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圖片資源處理
Android用到的圖片資源一般指三種:png/jpg等位圖文體,.9文件,selector xml文件,在之前的開發中,都放在drawable目錄下,但使用最新的And
Android源碼分析(二):View的事件分發機制探析
Android應用開發時,自定義控件時少不了和View的觸摸點擊事件打交道。針對View的事件分發原理,也看過網上的一些博客,但是看歸看,看了之後時間一長就又忘記了,因此
android 讀取WebView緩存及清理WebView緩存
WebView中存在著兩種緩存:網頁數據緩存(存儲打開過的頁面及資源)、H5緩存(即appcache)。一、網頁緩存1、緩存構成/data/data/package_na