主目錄見:Android高級進(jìn)階知識(shí)(這是總目錄索引)
?這個(gè)效果是之前看過的,正好是很典型的跟動(dòng)畫相關(guān)的例子谷朝,我這里就拿來跟大家分享一下,如果你以前看過又很熟悉動(dòng)畫那就可以略過娱局,如果不是很熟悉或者沒看過那不妨復(fù)習(xí)和學(xué)習(xí)一下瓷炮,上圖大家看下(好像轉(zhuǎn)化成gif不是很完全):
想要源代碼的可以[點(diǎn)擊下載],不過不熟悉的還是希望能自己敲敲。
一.目標(biāo)
?今天這篇文章就是純粹地使用一下屬性動(dòng)畫卸伞,進(jìn)而對前面源碼分析的補(bǔ)充抹镊,所以今天的目標(biāo)如下:
1.復(fù)習(xí)《屬性動(dòng)畫源碼分析(Choreographer"編舞者")》分析的原理和《屬性動(dòng)畫基礎(chǔ)用法》。
2.順便簡單看下貝塞爾曲線的用法瞪慧。
二.代碼分析
這個(gè)例子不是很麻煩髓考,我們首先就自定義一個(gè)BubbleLayout繼承RelativeLayout,為什么要繼承這個(gè)呢弃酌?其實(shí)因?yàn)樵诶^承ViewGroup的時(shí)候要強(qiáng)制重寫onLayout方法氨菇,這里沒有必要儡炼,所以繼承一個(gè)現(xiàn)成的布局。我們首先看到我們的構(gòu)造函數(shù):
public BubbleLayout(Context context) {
super(context);
init();
}
public BubbleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BubbleLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
發(fā)現(xiàn)構(gòu)造函數(shù)很簡單查蓉,就是調(diào)用了init()方法乌询,我們直接看init()做了什么?
private void init() {
initDrawable();
initInterpolator();
mParams = new LayoutParams(bubbleWidth,bubbleHeight);
mParams.addRule(CENTER_HORIZONTAL,TRUE);
mParams.addRule(ALIGN_PARENT_BOTTOM,TRUE);
}
我們看到初始化方法里面顯示調(diào)用了initDrawable()豌研,然后是initInterpolator()方法妹田,最后就是初始化Params(為了添加氣泡確定初始位置),我們先來看第一個(gè)方法吧:
private void initDrawable() {
redBubble = getResources().getDrawable(R.mipmap.red);
blueBubble = getResources().getDrawable(R.mipmap.blue);
yellowBubble = getResources().getDrawable(R.mipmap.yellow);
mDrawables = new Drawable[3];
mDrawables[0] = redBubble;
mDrawables[1] = blueBubble;
mDrawables[2] = yellowBubble;
bubbleWidth = redBubble.getIntrinsicWidth();
bubbleHeight = redBubble.getIntrinsicHeight();
}
這個(gè)地方主要是獲取幾個(gè)不同顏色的愛心,然后放進(jìn)Drawable數(shù)組里鹃共,因?yàn)楹竺嬉秒S機(jī)數(shù)來隨機(jī)取不同顏色的愛心鬼佣,最后是得到愛心的寬和高,后面會(huì)用到霜浴。接著我們看第二個(gè)方法:
private void initInterpolator() {
mInterpolators = new Interpolator[4];
mInterpolators[0] = new AccelerateDecelerateInterpolator();
mInterpolators[1] = new AccelerateInterpolator();
mInterpolators[2] = new DecelerateInterpolator();
mInterpolators[3] = new LinearInterpolator();
}
這個(gè)方法跟上面差不多晶衷,就是將不同的插值器放進(jìn)數(shù)組里面,到時(shí)會(huì)從數(shù)組中隨機(jī)取出插值器給上升動(dòng)畫阴孟。我們會(huì)在點(diǎn)擊按鈕的時(shí)候添加氣泡晌纫,所以我們看下添加氣泡的代碼:
public void addBubbles() {
ImageView bubble = new ImageView(getContext());
bubble.setImageDrawable(mDrawables[mRandom.nextInt(mDrawables.length)]);
bubble.setLayoutParams(mParams);
addView(bubble);
startAnimation(bubble);
}
首先實(shí)例化一個(gè)ImageView,然后設(shè)置圖片和Params永丝,添加進(jìn)這個(gè)自定義的ViewGroup锹漱,添加完就啟動(dòng)動(dòng)畫:
private void startAnimation(final ImageView bubble) {
ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(bubble,"alpha",0.2f,1f);
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(bubble,"scaleX",0.2f,1f);
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(bubble,"scaleY",0.2f,1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
startBezierAnimation(bubble);
}
});
animatorSet.setDuration(300);
animatorSet.playTogether(alphaAnimator,scaleXAnimator,scaleYAnimator);
animatorSet.start();
}
這里主要?jiǎng)赢嬘腥齻€(gè),首先是透明度變化慕嚷,然后是x和y的縮放哥牍。然后用AnimatorSet一起播放。動(dòng)畫播放完畢這周執(zhí)行上升的動(dòng)畫闯冷,下面會(huì)用到貝塞爾曲線知識(shí)砂心,這里我推薦一個(gè)模擬貝塞爾曲線運(yùn)動(dòng)路徑的在線網(wǎng)址如圖所示:
因?yàn)檫@個(gè)地方我們還需要自定義一個(gè)TypeEvaluator,我們首先看下貝塞爾曲線的公式:
這個(gè)公式不難蛇耀,我們根據(jù)這個(gè)公式來自定義一個(gè)TypeEvaluator如下:
public class BezierEvaluator implements TypeEvaluator<PointF> {
private PointF B,C;
public BezierEvaluator(PointF B,PointF C){
this.B = B;
this.C = C;
}
@Override
public PointF evaluate(float v, PointF A, PointF D) {
PointF evaluatedPoint = new PointF();
evaluatedPoint.x = A.x*(1-v)*(1-v)*(1-v) + B.x*3*(1-v)*(1-v)*v + C.x*3*(1-v)*v*v +D.x*v*v*v;
evaluatedPoint.y = A.y*(1-v)*(1-v)*(1-v) + B.y*3*(1-v)*(1-v)*v + C.y*3*(1-v)*v*v +D.y*v*v*v;
return evaluatedPoint;
}
}
這個(gè)很簡單就是套用上面的公式辩诞,一模一樣。然后我們就應(yīng)用這個(gè)估值器:
private void startBezierAnimation(final ImageView bubble) {
//兩個(gè)控制點(diǎn)隨意生成
PointF B = new PointF(mRandom.nextInt(width/2) + width/4,mRandom.nextInt(height/2) + height/2);
PointF C = new PointF(mRandom.nextInt(width/2) + width/4,mRandom.nextInt(height/2));
//起始點(diǎn)在這個(gè)布局的頂部中心點(diǎn),結(jié)束節(jié)點(diǎn)在頂部x為任意纺涤,y這里設(shè)置0高度
PointF A = new PointF((width - bubbleWidth)/2,height - bubbleHeight);
PointF D = new PointF(mRandom.nextInt(width),0);
BezierEvaluator bezierEvaluator = new BezierEvaluator(B,C);
ValueAnimator valueAnimator = ValueAnimator.ofObject(bezierEvaluator,A,D);
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
removeView(bubble);
}
});
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
PointF currentPoint = (PointF) valueAnimator.getAnimatedValue();
bubble.setX(currentPoint.x);
bubble.setY(currentPoint.y);
bubble.setAlpha(1 - valueAnimator.getAnimatedFraction());
}
});
valueAnimator.setTarget(bubble);
valueAnimator.setInterpolator(mInterpolators[mRandom.nextInt(mInterpolators.length)]);
valueAnimator.setDuration(2000);
valueAnimator.start();
}
這里代碼的B點(diǎn)和C點(diǎn)译暂,就是用上面推薦的一個(gè)網(wǎng)址模擬了下,感覺這個(gè)運(yùn)動(dòng)軌跡會(huì)好看點(diǎn)撩炊,然后A點(diǎn)是起始點(diǎn)就取控件中間位置外永,D點(diǎn)的x就是隨機(jī)的控件寬度,y就是頂部拧咳。然后隨后設(shè)置插值器setInterpolator伯顶,記住最后一定要?jiǎng)h除這個(gè)ImageView,避免添加太多的ImageView都沒有刪除掉。
總結(jié):文章到這里已經(jīng)說明完畢了祭衩,很簡單灶体,大家只要結(jié)合前面的基礎(chǔ)用法看下就明白了,沒啥難度掐暮。