Android 擴展-對任意的屬性做動畫

??我們都知道,在Android中,有5種基本動畫和屬性動畫,可以使得我們的View動起來。但是在Android中,特別是屬性動畫這種,可以改變View的屬性的動畫,如果想要對一個屬性使用動畫的前提是:這個屬性必須含有相應的get和set方法,那么如果想要對一些不符合上面要求的View使用屬性動畫,那該怎么辦呢?

1.引出問題

??比如,我們想要給一個Button加一個動畫,讓這個Button的寬度從當前的寬度變換到指定的寬度。也許你會說,這個非常的簡單啊,用View動畫就可以搞定了,我們可以來試試。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button mButton = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.id_button);
        mButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        ObjectAnimator objectAnimator = ObjectAnimator.ofInt(mButton, "width", 500);
        objectAnimator.setDuration(5000);
        objectAnimator.start();
    }
}

??如上面的代碼,我們使用了屬性動畫來改變Button的width。但是非常的奇怪,我們點擊Button,根本沒有任何的效果,同時上面還說了,屬性動畫如果想要改變一個View的屬性的話,那么這個屬性必須含有相應的set和get方法,但是我們通過API可以清楚看到Button含有setWidth和getWidth方法啊,但是為什么就是不行呢?

2.屬性動畫的原理

??我們先來分析一下屬性的動畫原理,在來解釋上面的問題產生的原因。
??屬性動畫要求作用的對象提供該屬性的get和set方法,屬性動畫genuine外界傳遞的該屬性的初始值和最終值,以動畫的效果多次去調用set方法,每次傳遞給set方法的值都是不一樣的,確切的說,是隨著時間的推移,所傳遞的值越來越接近最終值,總結一下,我們對object的屬性abc做動畫,如果想要動畫生效的話, 要求滿足下面兩個條件:

(1).object必須要提供setAbc方法,如果動畫的時候沒有傳遞初始值的話,那么還要提供getAbc方法,因為系統要去取abc屬性的初始值(如果這個條件不滿足的話,程序會直接Crash)。
(2).object的setAbc對屬性abc所做的改變必須能夠通過某種方法反應出來,比如會帶來UI的改變之類的(如果這個條件不滿足的話,動畫無效果但是不會Crash)。
通過上面的兩個條件,我們這里就可以分析一下上面我們通過屬性動畫來改變Button的width為什么沒有效果了。

??這個是因為Button內部雖然提供了getWidth和setWidth方法,但是這個setWidth方法并不是用來改變View的寬度的,它是TextView新添加的方法, View是沒有這個方法的,由于Button繼承于TextView,所以Button也就有了setWidth 方法。我們來看一看getWidth和setWidth的源代碼:

public void setWidth(int pixels) {
        mMaxWidth = mMinWidth = pixels;
        mMaxWidthMode = mMinWidthMode = PIXELS;

        requestLayout();
        invalidate();
    }
    @ViewDebug.ExportedProperty(category = "layout")
    public final int getWidth() {
        return mRight - mLeft;
    }

??從上面的的源代碼中可以看出,getWidth的確是獲取View的寬度的,但是setWidth是TextView及其子類才有的方法,它的作用不是設置View的寬度,而是設置TextView的最大寬度和最小寬度的,這個和TextView的寬度不是同一個東西。具體的來說,TextView的寬度對應的是XML文件中的android:layout_width屬性,而TextView還有一個屬性:android:width,這個屬性就對應了TextView中的setWidth方法。總之來說,TextView和Button的setWidth、getWidth干的不是同一件事,通過setWidth方法無法改變控件的寬度,所以對width做屬性動畫沒有效果的。

3.解決問題

??針對上面的問題,官方的文檔上告訴我們有三種方法來解決:

A.給你的對象加上get和set方法,如果你有權限的話。
B.用一個類來包裝原始對象,間接為其提供get和set方法。
C.采用ValueAnimator,監聽動畫過程,自己實現屬性的改變。

(1).給你的對象加上get和set方法,如果你有權限的話

??這個的意思很好理解,如果你有權限的話,加上get和set方法就搞定了。但是很多時候我們沒權限去這么做。就如本文所提出的問題,你無法給一個Button加上一個合乎要求的getWidth 方法,因為這個方法已經在Android 源代碼里面已經實現了。這個是最簡單的,但是往往是不可行的。

(2).用一個類來包裝原始對象,間接的為其提供get和set方法

??這個是很有用的解決方法,因為使用起來比較方便,也很好的理解,下面看一下簡單的例子

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button mButton = null;
    private ViewWrapper mViewWrapper = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.id_button);
        mButton.setOnClickListener(this);
        mViewWrapper = new ViewWrapper(mButton);
    }

    @Override
    public void onClick(View v) {
        ObjectAnimator objectAnimator = ObjectAnimator.ofInt(mViewWrapper, "width", 500);
        objectAnimator.setDuration(5000);
        objectAnimator.start();
    }
    //定義一個ViewWrapper類用來封裝View
    //這個類用來提供width屬性的get和set方法
    private class ViewWrapper {
        private View mTarget = null;

        public ViewWrapper(View target) {
            this.mTarget = target;
        }

        public void setWidth(int width) {
            this.mTarget.getLayoutParams().width = width;
            this.mTarget.requestLayout();

        }

        public int getWidth() {
            return mTarget.getLayoutParams().width;
        }
    }
}

(3).采用ValueAnimator,監聽動畫過程,自己實現屬性的改變

??首先說一說什么是ValueAnimator,ValueAnimator本身不作用與任何的對象,也就是說直接使用它沒有任何的動畫效果。它可以對一個值做動畫,然后我們可以監聽其動畫過程,在動畫過程中修改我們的對象的屬性值,這樣也就相當于我們的對象做了動畫。下面使用一個例子來說明一下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button mButton = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.id_button);
        mButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        perforAnimate(mButton, mButton.getWidth(), 400);
    }

    private void perforAnimate(final View target, final int start, final int end) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            //創建一個IntEvaluator對象--整數類型的數值估值器,用于下面的估值
            private IntEvaluator mIntEvaluator = new IntEvaluator();

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //獲取當前動畫的進度值,整型,1~100之間
                int currentValue = (Integer) animation.getAnimatedValue();
                //獲得當前進度占整個動畫過程的比例,浮點型,0~1之間
                float fraction = animation.getAnimatedFraction();
                target.getLayoutParams().width = mIntEvaluator.evaluate(fraction, start, end);
                target.requestLayout();
            }
        });
        valueAnimator.setDuration(1000).start();
    }
}
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容