• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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  */
16 
17 package android.animation;
18 
19 import android.util.StateSet;
20 import android.view.View;
21 
22 import java.lang.ref.WeakReference;
23 import java.util.ArrayList;
24 
25 /**
26  * Lets you define a number of Animators that will run on the attached View depending on the View's
27  * drawable state.
28  * <p>
29  * It can be defined in an XML file with the <code>&lt;selector></code> element.
30  * Each State Animator is defined in a nested <code>&lt;item></code> element.
31  *
32  * @attr ref android.R.styleable#DrawableStates_state_focused
33  * @attr ref android.R.styleable#DrawableStates_state_window_focused
34  * @attr ref android.R.styleable#DrawableStates_state_enabled
35  * @attr ref android.R.styleable#DrawableStates_state_checkable
36  * @attr ref android.R.styleable#DrawableStates_state_checked
37  * @attr ref android.R.styleable#DrawableStates_state_selected
38  * @attr ref android.R.styleable#DrawableStates_state_activated
39  * @attr ref android.R.styleable#DrawableStates_state_active
40  * @attr ref android.R.styleable#DrawableStates_state_single
41  * @attr ref android.R.styleable#DrawableStates_state_first
42  * @attr ref android.R.styleable#DrawableStates_state_middle
43  * @attr ref android.R.styleable#DrawableStates_state_last
44  * @attr ref android.R.styleable#DrawableStates_state_pressed
45  * @attr ref android.R.styleable#StateListAnimatorItem_animation
46  */
47 public class StateListAnimator {
48 
49     private final ArrayList<Tuple> mTuples = new ArrayList<Tuple>();
50 
51     private Tuple mLastMatch = null;
52 
53     private Animator mRunningAnimator = null;
54 
55     private WeakReference<View> mViewRef;
56 
57     private AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() {
58         @Override
59         public void onAnimationEnd(Animator animation) {
60             animation.setTarget(null);
61             if (mRunningAnimator == animation) {
62                 mRunningAnimator = null;
63             }
64         }
65     };
66 
67     /**
68      * Associates the given animator with the provided drawable state specs so that it will be run
69      * when the View's drawable state matches the specs.
70      *
71      * @param specs The drawable state specs to match against
72      * @param animator The animator to run when the specs match
73      */
addState(int[] specs, Animator animator)74     public void addState(int[] specs, Animator animator) {
75         Tuple tuple = new Tuple(specs, animator);
76         tuple.mAnimator.addListener(mAnimatorListener);
77         mTuples.add(tuple);
78     }
79 
80     /**
81      * Returns the current {@link android.animation.Animator} which is started because of a state
82      * change.
83      *
84      * @return The currently running Animator or null if no Animator is running
85      * @hide
86      */
getRunningAnimator()87     public Animator getRunningAnimator() {
88         return mRunningAnimator;
89     }
90 
91     /**
92      * @hide
93      */
getTarget()94     public View getTarget() {
95         return mViewRef == null ? null : mViewRef.get();
96     }
97 
98     /**
99      * Called by View
100      * @hide
101      */
setTarget(View view)102     public void setTarget(View view) {
103         final View current = getTarget();
104         if (current == view) {
105             return;
106         }
107         if (current != null) {
108             clearTarget();
109         }
110         if (view != null) {
111             mViewRef = new WeakReference<View>(view);
112         }
113 
114     }
115 
clearTarget()116     private void clearTarget() {
117         final int size = mTuples.size();
118         for (int i = 0; i < size; i++) {
119             mTuples.get(i).mAnimator.setTarget(null);
120         }
121 
122         mViewRef = null;
123         mLastMatch = null;
124         mRunningAnimator = null;
125     }
126 
127     /**
128      * Called by View
129      * @hide
130      */
setState(int[] state)131     public void setState(int[] state) {
132         Tuple match = null;
133         final int count = mTuples.size();
134         for (int i = 0; i < count; i++) {
135             final Tuple tuple = mTuples.get(i);
136             if (StateSet.stateSetMatches(tuple.mSpecs, state)) {
137                 match = tuple;
138                 break;
139             }
140         }
141         if (match == mLastMatch) {
142             return;
143         }
144         if (mLastMatch != null) {
145             cancel();
146         }
147         mLastMatch = match;
148         if (match != null) {
149             start(match);
150         }
151     }
152 
start(Tuple match)153     private void start(Tuple match) {
154         match.mAnimator.setTarget(getTarget());
155         mRunningAnimator = match.mAnimator;
156         mRunningAnimator.start();
157     }
158 
cancel()159     private void cancel() {
160         if (mRunningAnimator != null) {
161             mRunningAnimator.cancel();
162             mRunningAnimator = null;
163         }
164     }
165 
166     /**
167      * @hide
168      */
getTuples()169     public ArrayList<Tuple> getTuples() {
170         return mTuples;
171     }
172 
173     /**
174      * If there is an animation running for a recent state change, ends it.
175      * <p>
176      * This causes the animation to assign the end value(s) to the View.
177      */
jumpToCurrentState()178     public void jumpToCurrentState() {
179         if (mRunningAnimator != null) {
180             mRunningAnimator.end();
181         }
182     }
183 
184     /**
185      * @hide
186      */
187     public static class Tuple {
188 
189         final int[] mSpecs;
190 
191         final Animator mAnimator;
192 
Tuple(int[] specs, Animator animator)193         private Tuple(int[] specs, Animator animator) {
194             mSpecs = specs;
195             mAnimator = animator;
196         }
197 
198         /**
199          * @hide
200          */
getSpecs()201         public int[] getSpecs() {
202             return mSpecs;
203         }
204 
205         /**
206          * @hide
207          */
getAnimator()208         public Animator getAnimator() {
209             return mAnimator;
210         }
211     }
212 }
213