Android Button動畫與跳轉(zhuǎn)實(shí)現(xiàn)

背景:

近日瀏覽國外的網(wǎng)站發(fā)現(xiàn)了一個(gè)酷炫的按鈕動畫,想著自己造輪子實(shí)現(xiàn)汉嗽,同時(shí)也記錄下實(shí)現(xiàn)的過程欲逃。由于是第一次寫技術(shù)文章,不足之處請?jiān)谠u論區(qū)多多賜教饼暑。不勝感激稳析。
先上效果圖弓叛。

image

這個(gè)是在國外網(wǎng)站看到的。想自己實(shí)現(xiàn)按鈕動畫與跳轉(zhuǎn)撰筷。

步驟:

乍一看挺復(fù)雜,細(xì)細(xì)分析應(yīng)該是多種動畫效果的組合毕籽。剛開始點(diǎn)擊按鈕,按鈕由長條變?yōu)閳A形关筒,圓形按鈕中間有個(gè)白色的圓弧在不斷旋轉(zhuǎn),然后按鈕突然放大跳轉(zhuǎn)界面蒸播。整理一下。
1.按鈕由長條變圓形
2.白色的圓弧不斷旋轉(zhuǎn)
3.按鈕放大袍榆,跳轉(zhuǎn)界面

實(shí)現(xiàn):

首先定義一個(gè)類取名叫NbButton胀屿,讓其繼承Button,創(chuàng)建構(gòu)造方法,在init()方法中實(shí)現(xiàn)一些準(zhǔn)備工作包雀。

 public NbButton(Context context) {
    super(context);
    init(context);
}
public NbButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}
public NbButton(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
}

上圖中按鈕是粉色的宿崭,帶有圓角,所以這次在init()中直接定義顏色與圓角才写。

    backDrawable=new GradientDrawable();
    int colorDrawable=context.getColor(R.color.cutePink);
    backDrawable.setColor(colorDrawable);
    backDrawable.setCornerRadius(120);
    setBackground(backDrawable);

    setText("登陸");

GradientDrawable這個(gè)類用來定義shape屬性劳曹,關(guān)于GradientDrawable不作詳細(xì)介紹奴愉。backDrawable定義了按鈕的顏色,按鈕圓角半徑铁孵,并設(shè)置了字體。


1.png

然后該考慮動畫的事情了房资。

步驟1:

點(diǎn)擊按鈕后蜕劝,按鈕由長條變圓形。這個(gè)效果的原理是修改backDrawable屬性轰异,很多類似的自定義控件其形狀或顏色的變化原理都是修改Drawable屬性來實(shí)現(xiàn)岖沛。那么backDrawable發(fā)生了哪些變化?第一backDrawable的繪制范圍搭独,也就是bound變了婴削,由最初的長條范圍變化為可繪制圓的范圍。第二cornerRadius由最初的半徑變?yōu)锽utton高度的一半牙肝。通過ValueAnimator與ObjectAnimator來實(shí)現(xiàn)這些變化唉俗。
首先是bound的改變。

    ValueAnimator valueAnimator=ValueAnimator.ofInt(width,heigh);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int value= (int) animation.getAnimatedValue();
            int leftOffset=(width-value)/2;
            int rightOffset=width-leftOffset;

            backDrawable.setBounds(leftOffset,0,rightOffset,heigh);
        }
    });

ValueAnimator是個(gè)數(shù)值變化器配椭,只是改變值的大小虫溜,并不作用于動畫中屬性的改變,屬性的變化仍然需要我們手動設(shè)置股缸。width和heigh對應(yīng)繪制按鈕的寬和高衡楞。添加UpdateListener監(jiān)聽可以檢測到每次值的變化情況,


2.png

畫的不好請見諒敦姻。每次UpdateListener檢測到值變化時(shí)通過backDrawable.setBounds重新繪制backDrawable的范圍瘾境。
bound改變的同時(shí),cornerRadius也在改變镰惦。


3.png

cornerRadius從最初設(shè)置的120變?yōu)閔eigh的一半稠诲。
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(backDrawable,"cornerRadius",120,heigh/2);

ObjectAnimator是ValueAnimator的子類,可以直接改變相關(guān)的屬性完成動畫效果赡盘,相比較ValueAnimator捡遍,使用起來更加方便急膀。這里通過改變backDrawable的cornerRadius的屬性實(shí)現(xiàn)了圓角半徑的改變龄捡。
既然bound和cornerRadius同時(shí)改變聘殖,是不是兩個(gè)Animator一起start才可以實(shí)現(xiàn)呢行瑞?有一個(gè)類叫AnimatorSet血久,顧名思義就是Animator的集合帮非,通過它就可以使多個(gè)動畫協(xié)調(diào)工作末盔。

    AnimatorSet animatorSet=new AnimatorSet();
    animatorSet.setDuration(500);
    animatorSet.playTogether(valueAnimator,objectAnimator);
    animatorSet.start();

playTogether的方法中可以放入多個(gè)Animator,可以使多個(gè)動畫協(xié)調(diào)工作。調(diào)用start后翠拣,多個(gè)動畫開始工作误墓,步驟1就實(shí)現(xiàn)了优烧。

步驟2:

按鈕形狀改變的同時(shí)链峭,中間會出現(xiàn)個(gè)白色的圓弧弊仪,不斷旋轉(zhuǎn)。
咱們先畫一個(gè)白色的圓弧驳癌,利用canvas.drawArc這個(gè)方法就可以畫弧颓鲜。


4.png

圖中寬度的5/12.甜滨、7/12/瘤袖、1/7這些都不是真實(shí)計(jì)算得出的捂敌,只是感覺差不多是這樣既琴,請不要糾結(jié)為什么是寬度的5/12這些甫恩,請?jiān)徫业牟粐?yán)謹(jǐn)填物。霎终。莱褒。
在onDraw里面繪制白色的圓弧广凸,調(diào)用invalidate()方法重繪就有了效果谅海。

RectF rectF=new RectF(getWidth()*5/12,getHeight()/7,getWidth()*7/12,getHeight()getHeight()/7);
canvas.drawArc(rectF,0,270,false,paint);

關(guān)于paint畫筆的設(shè)置

    paint=new Paint();
    paint.setColor(getResources().getColor(R.color.white));
    paint.setStrokeWidth(4);
    paint.setStyle(Paint.Style.STROKE);
    paint.setTextSize(2);

ps:如果drawArc的第三個(gè)參數(shù)設(shè)置為true扭吁,圓弧就會過圓心盲镶。
圓弧算是畫出來了,那么如何實(shí)現(xiàn)圓弧不斷旋轉(zhuǎn)呢枫吧?熟悉drawArc這個(gè)方法的話很容易就能想到九杂,drawArc的第二個(gè)參數(shù)為圓弧的起始角度例隆,只需要不斷改變這個(gè)角度植影,就會形成旋轉(zhuǎn)的動畫思币。

    arcValueAnimator=ValueAnimator.ofInt(0,360);
    arcValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            startAngle= (int) animation.getAnimatedValue();
            invalidate();
        }
    });
    arcValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
    arcValueAnimator.setDuration(1000);
    arcValueAnimator.start();

把drawArc中第二個(gè)參數(shù)換成startAngle,在每次值更新后都重繪界面惶我。這樣圓弧的起始點(diǎn)就一直變化,形成了旋
轉(zhuǎn)效果盯蝴。設(shè)置animator無限循環(huán)捧挺,這樣效果就實(shí)現(xiàn)了闽烙,

 canvas.drawArc(rectF,startAngle,270,false,paint);

但是有個(gè)小問題声搁,圓弧的旋轉(zhuǎn)并不是勻速轉(zhuǎn),而是先加速后減速很魂,為什么呢遏匆?ValueAnimatro可以設(shè)置插值器Interpolator拉岁,插值器系統(tǒng)定義了好幾種惰爬,有accelerated(加速),decelerated(減速),repeated(重復(fù)),bounced(彈跳)LinearInterpolator(勻速)等陵叽。如果不設(shè)置巩掺,系統(tǒng)會默認(rèn)先加速后減速胖替。所以還需要設(shè)置插值器為勻速。

arcValueAnimator.setInterpolator(new LinearInterpolator());

這樣步驟2的效果也實(shí)現(xiàn)了端朵。

步驟3:

按鈕放大冲呢,以圓的形式向外擴(kuò)展招狸。這個(gè)動畫當(dāng)時(shí)也查了好久才發(fā)現(xiàn)了一個(gè)方法ViewAnimationUtils.createCircularReveal(View view裙戏,int centerX,int centerY翰意,float startRadius,float endRadius)醒第。第一個(gè)參數(shù)是當(dāng)前承載你動畫的view,就是這個(gè)按鈕無限放大形病,可支持他效果的view漠吻,這里我選根布局途乃,第二個(gè)參數(shù)是動畫中心x軸坐標(biāo)扔傅,就是按鈕的中心x軸坐標(biāo),第三個(gè)參數(shù)是動畫中心y軸坐標(biāo)试读,是按鈕的中心y坐標(biāo)钩骇,第四個(gè)參數(shù)是開始動畫開始的半徑,第五個(gè)是動畫結(jié)束的半徑银亲。

    int xc=(button.getLeft()+button.getRight())/2;
    int yc=(button.getTop()+button.getBottom())/2;
    animator= ViewAnimationUtils.createCircularReveal(rlContent,xc,yc,0,1111);

rlcontent是button的根布局群凶,寬度和高度都是占滿屏幕请梢,設(shè)置背景為按鈕放大的顏色毅弧,

 <RelativeLayout
    android:id="@+id/rl_content"
    android:background="@color/cutePink"
    android:layout_alignParentBottom="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.tongzhang.nbbutton.ui.NbButton
        android:id="@+id/button_test"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="100dp"
        android:textColor="#fff"
        android:text="登陸"
        android:layout_width="220dp"
        android:layout_height="50dp" />
</RelativeLayout>

設(shè)置根布局背景顏色為紅色后够坐,在oncreate中設(shè)置背景透明度為0元咙,這樣一進(jìn)入activity庶香,就不會出現(xiàn)滿屏幕紅色的情況简识。

 rlContent.getBackground().setAlpha(0);

當(dāng)按鈕開始無限放大后,再設(shè)置透明度為不透明即可顯示支持button的紅色區(qū)域奢赂。

    animator.setDuration(300);
    animator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    startActivity(intent);
                    overridePendingTransition(R.anim.anim_in,R.anim.anim_out);
                }
            },200);
        }
    });
    animator.start();
    rlContent.getBackground().setAlpha(255);

當(dāng)動畫開始后膳灶,使用handler準(zhǔn)備入場跳轉(zhuǎn)動畫立由。為什么不等待動畫結(jié)束后再進(jìn)行入場動畫呢?其實(shí)這種動畫執(zhí)行到一半就開始入場動畫效果比較好聋迎,大家可以自己試試霉晕。
至于入場動畫,貼上代碼牺堰。
anim.in.xml:

<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="0" android:toAlpha="1"
    android:duration="1000"/>
</set>

anim.out.xml:

<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1" android:toAlpha="0"
    android:duration="1000"/>
</set>

至此伟葫,整個(gè)動畫的思路就結(jié)束了筏养。
附上源碼:https://github.com/TongZhang1314/ButtonAnimator
很簡單的一個(gè)實(shí)現(xiàn),記錄下實(shí)現(xiàn)過程渐溶,權(quán)當(dāng)鞏固一下自定義view方面的知識茎辐。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拖陆,一起剝皮案震驚了整個(gè)濱河市依啰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖艰争,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸠匀,死亡現(xiàn)場離奇詭異,居然都是意外死亡缀棍,警方通過查閱死者的電腦和手機(jī)爬范,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門青瀑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斥难,“玉大人,你說我怎么就攤上這事群扶《瓶悖” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵馁菜,是天一觀的道長汪疮。 經(jīng)常有香客問我毁习,道長,這世上最難降的妖魔是什么盏道? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任猜嘱,我火速辦了婚禮嫁艇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘论皆。我一直安慰自己点晴,他們只是感情好悯周,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布坠陈。 她就那樣靜靜地躺著,像睡著了一般仇矾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姐仅,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音敦锌,去河邊找鬼。 笑死颖变,一個(gè)胖子當(dāng)著我的面吹牛腥刹,可吹牛的內(nèi)容都是我干的汉买。 我是一名探鬼主播蛙粘,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼出牧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起旺遮,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤边翼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后组底,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡江滨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棺弊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡稻艰,死狀恐怖尊勿,靈堂內(nèi)的尸體忽然破棺而出运怖,到底是詐尸還是另有隱情夏伊,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站鲁森,受9級特大地震影響祟滴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歌溉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一垄懂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痛垛,春花似錦草慧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蹂析,卻和暖如春舔示,著一層夾襖步出監(jiān)牢的瞬間碟婆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工竖共, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锻煌,地道東北人匣沼。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親窖认。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

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