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