[Material Design] 教你作一個Material風格、動畫的button(MaterialButton)

原創做品,轉載請註明出處:http://blog.csdn.net/qiujuer/article/details/39831451
java

前段時間Android L 公佈了,相信看過公佈會了解過的朋友都爲當中的 「Material Design」 感到由衷的驚豔吧!至少我是的。git

在驚豔之餘感到由衷的遺憾,因爲其必須在 」Android L「 上才幹使用。MD。鬱悶啊。
以後便本身想弄一個點擊動畫試試,此念頭一發不可收拾;乾脆一不作二不休,就重寫了一個 」MaterialButton「 控件出來。
在這裏不討論什麼是 :「Material Design」 。
在這裏將給你們分享一下我本身弄的 「Material Design」 風格的 」MaterialButton「 button動畫實現。

預熱一下:github


上面的兩張動畫相信你們都看過吧?是否是挺不錯的?反正我是認爲手機上有這種動畫是很是爽的,比較手機是用來添加體驗的。但是這些動畫僅僅能在Android L 才幹體驗到,對於現在國內的 Android 廠商的狀況來看,預計谷歌出新的版本號的時候咱們就能用上這個 L 版本號了。編程

如下給大夥看看我作的 「MaterialButton」 button:canvas


效果還不錯吧?好了開始開工了。ide

介紹一下個人工具:「Android Studio」 固然你們用其它也行。工具

第一步:新建項目(這個隨意。本身搗鼓吧)佈局

第二步:新建本身定義控件:在java目錄上右擊選擇本身定義控件:字體


取個名字:「MaterialButton動畫


現在來看看多了一個類(MaterialButton),一個佈局文件 「sample_material_button」,一個屬性文件 「attrs_material_button


到這裏第二步完畢了。多了3個文件。

第三步:改動 「MaterialButton」 類:

分爲幾步走:刪除演示樣例代碼又一次繼承自 「Button」 類複寫 「onTouchEvent()」 方法。完畢後的代碼:

public class MaterialButton extends Button {
    public MaterialButton(Context context) {
        super(context);
        init(null, 0);
    }

    public MaterialButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public MaterialButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        // Load attributes
        final TypedArray a = getContext().obtainStyledAttributes(
                attrs, R.styleable.MaterialButton, defStyle, 0);
        a.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

}
是否是感受乾淨多了?到此第三步完畢了。

第四步:就是作實際的動畫了,在這裏需要給你們說說三個需要注意的東西:

1.點擊事件響應,這個很是好理解,在 「onTouchEvent()」 方法中完畢,在該方法中咱們需要完畢的是點擊後啓動一個動畫。同一時候需要獲取到當時點擊的位置。

2.動畫,這裏的動畫不是放大動畫而是屬性動畫,說實話 這個要說清楚還真不是一點點就能說清楚的事情。簡單說就是在動畫中可以控制一個屬性的變化,而在這裏來講就是在 「MaterialButton」 類中創建一個寬度和一個顏色的屬性,而後在動畫中控制這兩個屬性的變化。

3.屬性的創建以及屬性的變化區域肯定問題。

首先創建兩個屬性:

    private Paint backgroundPaint;
    private float radius;
    private Property<MaterialButton, Float> mRadiusProperty = new Property<MaterialButton, Float>(Float.class, "radius") {
        @Override
        public Float get(MaterialButton object) {
            return object.radius;
        }

        @Override
        public void set(MaterialButton object, Float value) {
            object.radius = value;
            //刷新Canvas
            invalidate();
        }
    };

    private Property<MaterialButton, Integer> mBackgroundColorProperty = new Property<MaterialButton, Integer>(Integer.class, "bg_color") {
        @Override
        public Integer get(MaterialButton object) {
            return object.backgroundPaint.getColor();
        }

        @Override
        public void set(MaterialButton object, Integer value) {
            object.backgroundPaint.setColor(value);
        }
    };

兩個屬性對照一下可以發現在半徑的屬性 「set」 操做中調用了 「invalidate()」 方法,該方法的做用是告訴系統刷新當前控件的 「Canvas」。也就是觸發一次:「onDraw(Canvas canvas)」 方法。

而後複寫 「onTouchEvent()」 方法例如如下:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            //記錄座標
            paintX = event.getX();
            paintY = event.getY();
            //啓動動畫
            startAnimator();
        }
        return super.onTouchEvent(event);
    }
在該方法中,首先肯定是不是點擊下去的事件。而後記錄座標,並啓動動畫。

在啓動動畫方法 「startAnimator()」 方法中。咱們這樣寫:

    private void startAnimator() {
        
        //計算半徑變化區域
        int start, end;

        if (getHeight() < getWidth()) {
            start = getHeight();
            end = getWidth();
        } else {
            start = getWidth();
            end = getHeight();
        }

        float startRadius = (start / 2 > paintY ? start - paintY : paintY) * 1.15f;
        float endRadius = (end / 2 > paintX ? end - paintX : paintX) * 0.85f;

        //新建動畫
        AnimatorSet set = new AnimatorSet();
        //加入變化屬性
        set.playTogether(
                //半徑變化
                ObjectAnimator.ofFloat(this, mRadiusProperty, startRadius, endRadius),
                //顏色變化 黑色到透明
                ObjectAnimator.ofObject(this, mBackgroundColorProperty, new ArgbEvaluator(), Color.BLACK, Color.TRANSPARENT)
        );
        // 設置時間
        set.setDuration((long) (1200 / end * endRadius));
        //先快後慢
        set.setInterpolator(new DecelerateInterpolator());
        set.start();
    }

在這一步咱們需要知道有些button並不是橫向的,因此長不必定大於寬度。因此需要先推斷獲取到最長與最短,而後進行計算獲取到開始的半徑與結束的半徑。這裏有一個個人思路圖:


咱們知道在 Android 中都是以左上腳爲圓心。而後右邊爲X正數。下邊爲Y正數。

因此創建瞭如上座標系。

藍色矩形區域表明button。藍色點表明點擊的點。

灰色矩形表明點擊後的開始區域,而後4邊開始擴散開。以上就是一個簡單的原理。固然思路有些跳躍,假設不懂可以在下邊評論我都會進行回覆的。


第五步:畫畫。對就是畫畫。這一步就是利用上面的半徑和畫筆顏色進行實際的繪製。

這裏需要了解的是:

1:畫畫是在:「onDraw(Canvas canvas)」 方法中完畢

2:在畫板(Canvas)上是分層級的。簡單說就是先畫背景而後畫房子,而後畫人。最後畫人的一些小細節 自底向上的流程

3:畫板每次畫 都是新的畫板。預示着你每次都需要從背景畫起而後纔到人。在編程中就是每次 「onDraw(Canvas canvas)」 方法中的畫板(Canvas )都是新的(New)。

說了那麼多事實上很是easy,因爲複雜的都在上一步中完畢了。

 「onDraw(Canvas canvas)」 源代碼例如如下:

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        canvas.drawCircle(paintX, paintY, radius, backgroundPaint);
        canvas.restore();

        super.onDraw(canvas);
    }
在這裏咱們先保存了畫板的狀態。而後畫一個圓,而後恢復上一次的狀態,而後調用父類進行後面的繪製工做。

這裏解釋一下:

1.爲何 「super.onDraw(canvas)」 需要放在最後調用?

因爲畫板是分層級的,當調用 「super.onDraw(canvas)」 的時候進行的工做是繪製字體那些。假設放在前面調用那麼形成的後果是咱們的圓會覆蓋到字體上面。

因此咱們需要先畫圓背景。

2.爲何僅僅有一次畫圓操做(canvas.drawCircle())?

因爲在半徑屬性中調用了 「invalidate()」 ,當每次變化半徑值的時候將進行一次 「onDraw(canvas)」 操做,也就畫一次圓,在必定時間內高速反覆畫半徑逐漸增大的圓的時候就造成了動畫效果。

最後給出此次控件的代碼:

public class MaterialButton extends Button {
    private Paint backgroundPaint;
    private float paintX, paintY, radius;

    public MaterialButton(Context context) {
        super(context);
        init(null, 0);
    }

    public MaterialButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public MaterialButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        // Load attributes
        final TypedArray a = getContext().obtainStyledAttributes(
                attrs, R.styleable.MaterialButton, defStyle, 0);
        a.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        canvas.drawCircle(paintX, paintY, radius, backgroundPaint);
        canvas.restore();

        super.onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            //記錄座標
            paintX = event.getX();
            paintY = event.getY();
            //啓動動畫
            startAnimator();
        }
        return super.onTouchEvent(event);
    }

    private void startAnimator() {

        //計算半徑變化區域
        int start, end;

        if (getHeight() < getWidth()) {
            start = getHeight();
            end = getWidth();
        } else {
            start = getWidth();
            end = getHeight();
        }

        float startRadius = (start / 2 > paintY ?

start - paintY : paintY) * 1.15f; float endRadius = (end / 2 > paintX ? end - paintX : paintX) * 0.85f; //新建動畫 AnimatorSet set = new AnimatorSet(); //加入變化屬性 set.playTogether( //半徑變化 ObjectAnimator.ofFloat(this, mRadiusProperty, startRadius, endRadius), //顏色變化 黑色到透明 ObjectAnimator.ofObject(this, mBackgroundColorProperty, new ArgbEvaluator(), Color.BLACK, Color.TRANSPARENT) ); // 設置時間 set.setDuration((long) (1200 / end * endRadius)); //先快後慢 set.setInterpolator(new DecelerateInterpolator()); set.start(); } private Property<MaterialButton, Float> mRadiusProperty = new Property<MaterialButton, Float>(Float.class, "radius") { @Override public Float get(MaterialButton object) { return object.radius; } @Override public void set(MaterialButton object, Float value) { object.radius = value; //刷新Canvas invalidate(); } }; private Property<MaterialButton, Integer> mBackgroundColorProperty = new Property<MaterialButton, Integer>(Integer.class, "bg_color") { @Override public Integer get(MaterialButton object) { return object.backgroundPaint.getColor(); } @Override public void set(MaterialButton object, Integer value) { object.backgroundPaint.setColor(value); } }; }


固然興許的工做還有:不一樣的顏色的buttonbutton屬性的問題

介於你們可能沒有 Android Studio 沒法看到效果,特地把 Apk 上傳了,假設Eclipse不知道怎麼導入的話 就加我QQ。我給你說一下!

地址:APK

這些我都在我的的項目中完畢了。你們拿去試試:

Genius-Android


進階:[Material Design] MaterialButton 效果進階 動畫本身主動移動進行對齊效果

相關文章
相關標籤/搜索