1. 程式人生 > >Android Reveal Animation(揭露動畫)實現

Android Reveal Animation(揭露動畫)實現

Android L (21)添加了揭露動畫,我們先來看一下效果圖:
這裡寫圖片描述
看起來是不是蠻爽的~~
接下來我們看一下怎麼實現這種效果把
首先我們新建一個Android專案,開啟對應的layout,寫成這樣:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
<!--做動畫的佈局--> <RelativeLayout android:id="@+id/targetView" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:background="#ff00ff" android:layout_width="300dp" android:gravity="center" android:layout_height="400dp"
>
<!--啟動動畫按鈕--> <Button android:id="@+id/start" android:text="哈哈" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout> </LinearLayout>

然後我們在Activity裡新增這樣的程式碼:

final
View targetView = findViewById(R.id.targetView); findViewById(R.id.start).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final int width = targetView.getMeasuredWidth(); final int height = targetView.getMeasuredHeight(); final float radius = (float)Math.sqrt(width*width + height*height) / 2;//半徑 Animator animator = ViewAnimationUtils.createCircularReveal(targetView, width/2, height/2, radius, 0); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { targetView.setVisibility(View.GONE); } }); animator.setDuration(2000l); animator.start(); } });

然後我們執行一下就可以看到想要的效果了~~~

然而我們平時開發卻並不是這麼如意,設計師不會讓我們做這麼簡單的動畫的,一定會有很多要求,然後發現這個就沒辦法做到了~~
我們再來看一下sdk提供的方法:

public static Animator createCircularReveal(View view,
            int centerX,  int centerY, float startRadius, float endRadius) {
       ...
    }

通過引數來看,要做動畫的view,中心座標, 起始半徑
感覺能做的事情太少了,比如我有個需要要求做動畫時中心座標要做一個位移,類似這樣:
這裡寫圖片描述
為了方便看圓心位置,特意畫了個藍色小點標識一下,如果要實現這種可定製畫的操作我們又該如何實現呢?我這裡提供一種思路,當然方法應該還有其他的方式~
我這裡用到了ValueAnimator動畫類,這個類可以幫助我們算動畫進度百分比,然後根據比例進行相應的圖形處理即可~

第一步:

新建一個動畫布局類:CircleAnimationLayout
這裡我繼承了FrameLayout,這樣的話,我們只要包裹想要做動畫的View即可,不管是View還是ViewGroup都可以了就。
來看一下這個類程式碼(先宣告一下,這裡寫的比較隨意,大家在專案裡可以寫的更完整一些,規範一些要):

public class CircleAnimationLayout extends FrameLayout {
    private Point mStartPoint;//起始圓心x,y
    private Point mEndPoint;//結束圓心x,y
    private float mStartR;//起始圓半徑
    private float mEndR;//結束圓半徑

    private float dx, dy, dr;// 用於存放x y r的變化值
    private Animator.AnimatorListener mListener;//動畫回撥
    private Paint mPaint;// 畫筆,畫那個藍色小圓,標記用
    private Path mPath;// 用於裁剪view
    private boolean isClipView = false;//標識是否被裁剪,即不動畫不需要裁剪

    public CircleAnimationLayout(Context context) {
        this(context, null);
    }

    public CircleAnimationLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleAnimationLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);//這個必須加在ViewGroup裡,否則不會呼叫OnDraw方法
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(0xff0000ff);//圓心顏色
        mPath = new Path();
    }

    public CircleAnimationLayout setStartPoint(Point point) {
        mStartPoint = point;
        return this;//非正規建造模式
    }

    public CircleAnimationLayout setEndPoint(Point point) {
        mEndPoint = point;
        return this;//非正規建造模式
    }

    public CircleAnimationLayout setStartR(float r) {
        mStartR = r;
        return this;//非正規建造模式
    }
    public CircleAnimationLayout setEndR(float r) {
        mEndR = r;
        return this;//非正規建造模式
    }

    public CircleAnimationLayout setOnAnimatorListener(Animator.AnimatorListener listener) {
        mListener = listener;
        return this;//非正規建造模式
    }

    public void startAnimation() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(2000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction(); // 百分比
                dx = fraction * (mEndPoint.x - mStartPoint.x);
                dy = fraction * (mEndPoint.y - mStartPoint.y);
                dr = fraction * (mEndR - mStartR);
                Log.e("yk3372", dx + " " + dy + " " + dr);
                invalidate();
            }
        });
        animator.addListener(mListener);//設定動畫回撥
        animator.start();
        isClipView = true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 重點在這,上面所有都是為了這服務的
        if (isClipView) {
            float x = mStartPoint.x + dx;//臨時x座標
            float y = mStartPoint.y + dy;//臨時y座標
            float r = mStartR + dr;//臨時半徑r
            mPath.reset();
            mPath.addCircle(x, y, r, Path.Direction.CW);// 設定path為圓,我們可以任意定義各種圖形
            canvas.clipPath(mPath);// 對view進行裁剪! 重點在這
            canvas.drawCircle(x, y, 10, mPaint); // 畫那個藍色圓心用的
        }
    }
}

第二步:

至於佈局呢,就直接把剛才那個給套在這個View下即可:

<!--包裹即可-->
<com.example.yukai.test.CircleAnimationLayout
        android:id="@+id/targetView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <RelativeLayout
            android:layout_width="300dp"
            android:layout_height="400dp"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="10dp"
            android:background="#66ff00ff"
            android:gravity="center">

            <Button
                android:id="@+id/start"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="哈哈" />

        </RelativeLayout>
    </com.example.yukai.test.CircleAnimationLayout>

第三步:

然後我們再來看一下使用方式:

final CircleAnimationLayout targetView = (CircleAnimationLayout) findViewById(R.id.targetView);

        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final int width = targetView.getMeasuredWidth();
                final int height = targetView.getMeasuredHeight();
                final float radius = (float) Math.sqrt(width * width + height * height);
                targetView.setStartPoint(new Point(width / 2, height / 2))// 外面看起來跟建造模式使用一樣
                        .setEndPoint(new Point(width / 2, 0))
                        .setStartR(radius/2)
                        .setEndR(0)
                        .setOnAnimatorListener(new AnimatorListenerAdapter() {
                            @Override
                            public void onAnimationEnd(Animator animation) {
                                targetView.setVisibility(View.GONE);//動畫結束後就可以隱藏之類的操作了~
                            }
                        });
                targetView.startAnimation();
            }
        });

ok,通過上面的學習,我們其實可以做很多各種各樣的動畫,要感謝ValueAnimator這個動畫類,是我們實現動畫很簡單。在不知道這個類之前,我曾經嘗試自己去計算這些值,當然還要考慮進插值器(Interpolator),有點麻煩就不寫了,用ValueAnimator足夠了~~
趕緊自己去試一下吧~~^_^