動(dòng)畫框架(3)-直播間氣泡效果

主目錄見: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ǔ)用法看下就明白了,沒啥難度掐暮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝎抽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子路克,更是在濱河造成了極大的恐慌樟结,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件精算,死亡現(xiàn)場離奇詭異瓢宦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)殖妇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門刁笙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來破花,“玉大人谦趣,你說我怎么就攤上這事∽浚” “怎么了前鹅?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長峭梳。 經(jīng)常有香客問我舰绘,道長,這世上最難降的妖魔是什么葱椭? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任捂寿,我火速辦了婚禮,結(jié)果婚禮上孵运,老公的妹妹穿的比我還像新娘秦陋。我一直安慰自己,他們只是感情好治笨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布驳概。 她就那樣靜靜地躺著,像睡著了一般旷赖。 火紅的嫁衣襯著肌膚如雪顺又。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天等孵,我揣著相機(jī)與錄音稚照,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛果录,可吹牛的內(nèi)容都是我干的腌闯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼雕憔,長吁一口氣:“原來是場噩夢啊……” “哼姿骏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起斤彼,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤分瘦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后琉苇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘲玫,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年并扇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了去团。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡穷蛹,死狀恐怖土陪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肴熏,我是刑警寧澤鬼雀,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蛙吏,受9級特大地震影響源哩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸦做,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一励烦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泼诱,春花似錦坛掠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至矢炼,卻和暖如春系瓢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背句灌。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工夷陋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留欠拾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓骗绕,卻偏偏與公主長得像藐窄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子酬土,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容