1. 程式人生 > >Android仿直播特效之點贊飄心

Android仿直播特效之點贊飄心

一、概述

老規矩先上圖


好了,基本就是這個樣子,錄完的視訊用格式工廠轉換完就這個樣子了,將就看吧

二、定義我們自己的Layout

/**
 * @author 劉洋巴金
 * @date 2017-4-27
 * 
 * 定義我們自己的佈局
 * */
public class LoveLayout extends RelativeLayout{

	private Context context;
	private LayoutParams params;
	private Drawable[] icons = new Drawable[4];
	private Interpolator[] interpolators = new Interpolator[4];
	private int mWidth;
	private int mHeight;
	
	public LoveLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		this.context = context;
		initView();
	}

	private void initView() {
		
		// 圖片資源
		icons[0] = getResources().getDrawable(R.drawable.green);
		icons[1] = getResources().getDrawable(R.drawable.purple);
		icons[2] = getResources().getDrawable(R.drawable.red);
		icons[3] = getResources().getDrawable(R.drawable.yellow);
		
		// 插值器
		interpolators[0] = new AccelerateDecelerateInterpolator(); // 在動畫開始與結束的地方速率改變比較慢,在中間的時候加速
		interpolators[1] = new AccelerateInterpolator();  // 在動畫開始的地方速率改變比較慢,然後開始加速
		interpolators[2] = new DecelerateInterpolator(); // 在動畫開始的地方快然後慢
		interpolators[3] = new LinearInterpolator();  // 以常量速率改變
		
		int width = icons[0].getIntrinsicWidth();
		int height = icons[0].getIntrinsicWidth();
		params = new LayoutParams(width, height);
		params.addRule(CENTER_HORIZONTAL, TRUE);
		params.addRule(ALIGN_PARENT_BOTTOM, TRUE);
	}
基本就是做了初始化,聲明瞭4個drawable,也就是4個顏色的心,4個插值器,用於控制動畫速率的改變,設定初始位置為螢幕的下邊中點處。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	// TODO Auto-generated method stub
	super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	mWidth = getMeasuredWidth();
	mHeight = getMeasuredHeight();
}
	
public void addLoveView() {
	// TODO Auto-generated method stub
	final ImageView iv = new ImageView(context);
	iv.setLayoutParams(params);
	iv.setImageDrawable(icons[new Random().nextInt(4)]);
	addView(iv);
		
	// 開啟動畫,並且用完銷燬
	AnimatorSet set = getAnimatorSet(iv);
	set.start();
	set.addListener(new AnimatorListenerAdapter() {
		@Override
		public void onAnimationEnd(Animator animation) {
			// TODO Auto-generated method stub
			super.onAnimationEnd(animation);
			removeView(iv);
		}
	});
}
用於新增心型效果。動畫結束後,再移除
/**
 * 獲取動畫集合
 * @param iv 
 * */
private AnimatorSet getAnimatorSet(ImageView iv) {
		
	// 1.alpha動畫
	ObjectAnimator alpha = ObjectAnimator.ofFloat(iv, "alpha", 0.3f, 1f);
		
	// 2.縮放動畫
	ObjectAnimator scaleX = ObjectAnimator.ofFloat(iv, "scaleX", 0.2f, 1f);
	ObjectAnimator scaleY = ObjectAnimator.ofFloat(iv, "scaleY", 0.2f, 1f);
		
	// 動畫集合
	AnimatorSet set = new AnimatorSet();
	set.playTogether(alpha, scaleX, scaleY);
	set.setDuration(500);
		
	// 貝塞爾曲線動畫
	ValueAnimator bzier = getBzierAnimator(iv);
		
	AnimatorSet set2 = new AnimatorSet();
	set2.playSequentially(set, bzier);
	set2.setTarget(iv);
	return set2;
}
playTogether:幾個動畫同時執行

ObjectAnimator為屬性動畫,不熟悉可以百度瞭解下

然後是設定貝塞爾曲線動畫

playSequentially:動畫依次執行

/**
 * 貝塞爾動畫
 * */
private ValueAnimator getBzierAnimator(final ImageView iv) {
		// TODO Auto-generated method stub
	PointF[] PointFs = getPointFs(iv); // 4個點的座標
	BasEvaluator evaluator = new BasEvaluator(PointFs[1], PointFs[2]);
	ValueAnimator valueAnim = ValueAnimator.ofObject(evaluator, PointFs[0], PointFs[3]);
	valueAnim.addUpdateListener(new AnimatorUpdateListener() {
			
		@Override
		public void onAnimationUpdate(ValueAnimator animation) {
			// TODO Auto-generated method stub
			PointF p = (PointF) animation.getAnimatedValue();
			iv.setX(p.x);
			iv.setY(p.y);
			iv.setAlpha(1- animation.getAnimatedFraction()); // 透明度
		}
	});
	valueAnim.setTarget(iv);
	valueAnim.setDuration(3000);
	valueAnim.setInterpolator(interpolators[new Random().nextInt(4)]);
	return valueAnim;
}

private PointF[] getPointFs(ImageView iv) {
	// TODO Auto-generated method stub
	PointF[] PointFs = new PointF[4];
	PointFs[0] = new PointF(); // p0
	PointFs[0].x = (mWidth- params.width)/ 2;
	PointFs[0].y = mHeight - params.height;
	
	PointFs[1] = new PointF(); // p1
	PointFs[1].x = new Random().nextInt(mWidth);  
	PointFs[1].y = new Random().nextInt(mHeight /2) + mHeight / 2 + params.height;
	
	PointFs[2] = new PointF(); // p2
	PointFs[2].x = new Random().nextInt(mWidth);  
	PointFs[2].y = new Random().nextInt(mHeight /2);
		
	PointFs[3] = new PointF(); // p3
	PointFs[3].x = new Random().nextInt(mWidth);  
	PointFs[3].y = 0;
	return PointFs;
}
先獲得4個點的座標


p0座標:x座標((佈局的寬-心形圖片寬)除以2),y座標(佈局的高 -心形圖片高),這樣獲得的是頂部部水平中心點的座標。

p1座標:x座標(橫座標中的隨機位置),y座標(佈局一半的高度 加上 0到一半高度範圍內的隨機座標+心形的高度的一半)。這樣取到的橫座標是在佈局寬度之內的隨機座標,縱座標為整個路徑高度中部以上的隨機座標。

p2座標:與p1類似,橫座標是在佈局寬度之內的隨機座標,縱座標為整個路徑高度中部以下的隨機座標。

p3座標:控制元件底部中心點

好了知道4個座標了,那麼開始計算路徑

首先為了計算貝塞爾曲線,我們先寫一個估值器

/**
 * @author 劉洋巴金
 * @date 2017-4-27
 * 
 * 估值器,計算路徑
 * */
public class BasEvaluator implements TypeEvaluator<PointF> {

	private PointF p1;
	private PointF p2;

	public BasEvaluator(PointF p1, PointF p2) {
		super();
		this.p1 = p1;
		this.p2 = p2;
	}

	@Override
	public PointF evaluate(float fraction, PointF p0, PointF p3) {
		// TODO Auto-generated method stub
		PointF pointf = new PointF();
		
		// 貝塞爾曲線公式  p0*(1-t)^3 + 3p1*t*(1-t)^2 + 3p2*t^2*(1-t) + p3^3
		pointf.x = p0.x * (1-fraction) *(1-fraction ) * (1-fraction)
				   +3*p1.x * fraction *(1-fraction )*(1-fraction )
				   +3*p2.x *fraction  *fraction  *(1-fraction )
				   +p3.x*fraction *fraction *fraction ; 
		pointf.y = p0.y * (1-fraction ) *(1-fraction ) * (1-fraction )
				+3*p1.y * fraction *(1-fraction )*(1-fraction )
				+3*p2.y *fraction  *fraction  *(1-fraction )
				+p3.y*fraction *fraction *fraction ; 
		return pointf;
	}
}
TypeEvaluator:估值器回撥evaluate方法,用於動態的改變動畫的屬性值。

evaluate三個引數:

1.fraction,預設傳入的就是(currentTime - startTime) / duration,動畫執行的時間除以總的時間比值,可以理解為變化率。當duration到了的時候,正好,起始點變到終點。

2.起始點

3.終點

根據三個引數,計算點的根據每毫秒的變化率,計算點的路徑軌跡。

好了貝塞爾曲線動畫就講完了,然後再把動畫繫結到控制元件上。

最後在MainActivity中根據點選事件,進行增加心型就好了。

btn_press = (Button)findViewById(R.id.btn_press);
ll_love = (LoveLayout)findViewById(R.id.ll_love);
btn_press.setOnClickListener(new OnClickListener() {
			
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		ll_love.addLoveView();
	}
});

三、Demo