TransitionAnimationView.java revision 2f41fe6bcc8d055bf53ad070425552fad57dff21
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.contacts.widget;
17
18import com.android.contacts.R;
19
20import android.animation.Animator;
21import android.animation.Animator.AnimatorListener;
22import android.animation.AnimatorInflater;
23import android.content.Context;
24import android.content.res.TypedArray;
25import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.Color;
28import android.graphics.Rect;
29import android.graphics.drawable.BitmapDrawable;
30import android.util.AttributeSet;
31import android.view.View;
32import android.widget.FrameLayout;
33
34/**
35 * A container for a view that needs to have exit/enter animations when rebinding data.
36 * This layout should have a single child.  Just before rebinding data that child
37 * should make this call:
38 * <pre>
39 *   TransitionAnimationView.startAnimation(this);
40 * </pre>
41 */
42public class TransitionAnimationView extends FrameLayout implements AnimatorListener {
43
44    private View mPreviousStateView;
45    private Bitmap mPreviousStateBitmap;
46    private int mEnterAnimationId;
47    private int mExitAnimationId;
48    private int mAnimationDuration;
49    private Rect mClipMargins = new Rect();
50    private Rect mClipRect = new Rect();
51    private Animator mEnterAnimation;
52    private Animator mExitAnimation;
53
54    public TransitionAnimationView(Context context) {
55        this(context, null, 0);
56    }
57
58    public TransitionAnimationView(Context context, AttributeSet attrs) {
59        this(context, attrs, 0);
60    }
61
62    public TransitionAnimationView(Context context, AttributeSet attrs, int defStyle) {
63        super(context, attrs, defStyle);
64
65        TypedArray a = getContext().obtainStyledAttributes(
66                attrs, R.styleable.TransitionAnimationView);
67
68        mEnterAnimationId = a.getResourceId(R.styleable.TransitionAnimationView_enterAnimation,
69                android.R.animator.fade_in);
70        mExitAnimationId = a.getResourceId(R.styleable.TransitionAnimationView_exitAnimation,
71                android.R.animator.fade_out);
72        mClipMargins.left = a.getDimensionPixelOffset(
73                R.styleable.TransitionAnimationView_clipMarginLeft, 0);
74        mClipMargins.top = a.getDimensionPixelOffset(
75                R.styleable.TransitionAnimationView_clipMarginTop, 0);
76        mClipMargins.right = a.getDimensionPixelOffset(
77                R.styleable.TransitionAnimationView_clipMarginRight, 0);
78        mClipMargins.bottom = a.getDimensionPixelOffset(
79                R.styleable.TransitionAnimationView_clipMarginBottom, 0);
80        mAnimationDuration = a.getInt(
81                R.styleable.TransitionAnimationView_animationDuration, 100);
82
83        a.recycle();
84
85        mPreviousStateView = new View(context);
86        mPreviousStateView.setVisibility(View.INVISIBLE);
87        addView(mPreviousStateView);
88
89        mEnterAnimation = AnimatorInflater.loadAnimator(getContext(), mEnterAnimationId);
90        if (mEnterAnimation == null) {
91            throw new IllegalArgumentException("Invalid enter animation: " + mEnterAnimationId);
92        }
93        mEnterAnimation.addListener(this);
94        mEnterAnimation.setDuration(mAnimationDuration);
95
96        mExitAnimation = AnimatorInflater.loadAnimator(getContext(), mExitAnimationId);
97        if (mExitAnimation == null) {
98            throw new IllegalArgumentException("Invalid exit animation: " + mExitAnimationId);
99        }
100        mExitAnimation.setDuration(mAnimationDuration);
101    }
102
103    @Override
104    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
105        super.onLayout(changed, left, top, right, bottom);
106        if (changed || mPreviousStateBitmap == null) {
107            if (mPreviousStateBitmap != null) {
108                mPreviousStateBitmap.recycle();
109                mPreviousStateBitmap = null;
110            }
111            int width = right - left;
112            int height = bottom - top;
113            if (width > 0 && height > 0) {
114                mPreviousStateBitmap = Bitmap.createBitmap(
115                        width, height, Bitmap.Config.ARGB_8888);
116                mPreviousStateView.setBackgroundDrawable(
117                        new BitmapDrawable(getContext().getResources(), mPreviousStateBitmap));
118                mClipRect.set(mClipMargins.left, mClipMargins.top,
119                        width - mClipMargins.right, height - mClipMargins.bottom);
120            } else {
121                mPreviousStateBitmap = null;
122                mPreviousStateView.setBackgroundDrawable(null);
123            }
124        }
125    }
126
127    @Override
128    protected void onDetachedFromWindow() {
129        super.onDetachedFromWindow();
130        mPreviousStateView.setBackgroundDrawable(null);
131        if (mPreviousStateBitmap != null) {
132            mPreviousStateBitmap.recycle();
133            mPreviousStateBitmap = null;
134        }
135    }
136
137    public void startTransition(View view, boolean closing) {
138        if (mEnterAnimation.isRunning()) {
139            mEnterAnimation.end();
140        }
141        if (mExitAnimation.isRunning()) {
142            mExitAnimation.end();
143        }
144        if (view.getVisibility() != View.VISIBLE) {
145            if (!closing) {
146                mEnterAnimation.setTarget(view);
147                mEnterAnimation.start();
148            }
149        } else if (closing) {
150            mExitAnimation.setTarget(view);
151            mExitAnimation.start();
152        } else {
153            if (mPreviousStateBitmap == null) {
154                return;
155            }
156
157            mPreviousStateBitmap.eraseColor(Color.TRANSPARENT);
158            Canvas canvas = new Canvas(mPreviousStateBitmap);
159            canvas.clipRect(mClipRect);
160            view.draw(canvas);
161            canvas.setBitmap(null);
162            mPreviousStateView.setVisibility(View.VISIBLE);
163
164            mEnterAnimation.setTarget(view);
165            mEnterAnimation.start();
166        }
167    }
168
169    @Override
170    public void onAnimationEnd(Animator animation) {
171        mPreviousStateView.setVisibility(View.INVISIBLE);
172    }
173
174    @Override
175    public void onAnimationCancel(Animator animation) {
176    }
177
178    @Override
179    public void onAnimationStart(Animator animation) {
180    }
181
182    @Override
183    public void onAnimationRepeat(Animator animation) {
184    }
185}
186