diff options
| author | 2014-05-09 13:17:52 -0700 | |
|---|---|---|
| committer | 2014-05-13 13:07:06 -0700 | |
| commit | 30da61d477bcb6cc7718f9516c444359352fe148 (patch) | |
| tree | 0d13170ad1a3e058b954591c0b451aa5f53830ec | |
| parent | abb352a941cbd87c14cce9ccfa83157b913d41f2 (diff) | |
Add view name to Transition matching.
Bug 14625214
Change-Id: Ia1b21e6bd0ea4892b504746e582aeb175e0f0506
| -rw-r--r-- | api/current.txt | 8 | ||||
| -rw-r--r-- | core/java/android/transition/Transition.java | 610 | ||||
| -rw-r--r-- | core/java/android/transition/TransitionInflater.java | 9 | ||||
| -rw-r--r-- | core/java/android/transition/TransitionSet.java | 30 | ||||
| -rw-r--r-- | core/java/android/transition/TransitionValuesMaps.java | 6 | ||||
| -rw-r--r-- | core/java/android/transition/Visibility.java | 29 | ||||
| -rw-r--r-- | core/res/res/values/attrs.xml | 8 | ||||
| -rw-r--r-- | core/res/res/values/public.xml | 2 |
8 files changed, 428 insertions, 274 deletions
diff --git a/api/current.txt b/api/current.txt index 570aead0f4a1..8e8072f459e6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -505,6 +505,7 @@ package android { field public static final int excludeClass = 16843849; // 0x1010449 field public static final int excludeFromRecents = 16842775; // 0x1010017 field public static final int excludeId = 16843848; // 0x1010448 + field public static final int excludeViewName = 16843860; // 0x1010454 field public static final int exitFadeDuration = 16843533; // 0x101030d field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052 field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053 @@ -1127,6 +1128,7 @@ package android { field public static final int targetId = 16843740; // 0x10103dc field public static final int targetPackage = 16842785; // 0x1010021 field public static final int targetSdkVersion = 16843376; // 0x1010270 + field public static final int targetViewName = 16843859; // 0x1010453 field public static final int taskAffinity = 16842770; // 0x1010012 field public static final int taskCloseEnterAnimation = 16842942; // 0x10100be field public static final int taskCloseExitAnimation = 16842943; // 0x10100bf @@ -29045,6 +29047,7 @@ package android.transition { ctor public Transition(); method public android.transition.Transition addListener(android.transition.Transition.TransitionListener); method public android.transition.Transition addTarget(int); + method public android.transition.Transition addTarget(java.lang.String); method public android.transition.Transition addTarget(java.lang.Class); method public android.transition.Transition addTarget(android.view.View); method public boolean canRemoveViews(); @@ -29056,6 +29059,7 @@ package android.transition { method public android.transition.Transition excludeChildren(android.view.View, boolean); method public android.transition.Transition excludeChildren(java.lang.Class, boolean); method public android.transition.Transition excludeTarget(int, boolean); + method public android.transition.Transition excludeTarget(java.lang.String, boolean); method public android.transition.Transition excludeTarget(android.view.View, boolean); method public android.transition.Transition excludeTarget(java.lang.Class, boolean); method public long getDuration(); @@ -29066,12 +29070,16 @@ package android.transition { method public android.transition.TransitionPropagation getPropagation(); method public long getStartDelay(); method public java.util.List<java.lang.Integer> getTargetIds(); + method public java.util.List<java.lang.Class> getTargetTypes(); + method public java.util.List<java.lang.String> getTargetViewNames(); method public java.util.List<android.view.View> getTargets(); method public java.lang.String[] getTransitionProperties(); method public android.transition.TransitionValues getTransitionValues(android.view.View, boolean); method public android.transition.Transition removeListener(android.transition.Transition.TransitionListener); method public android.transition.Transition removeTarget(int); + method public android.transition.Transition removeTarget(java.lang.String); method public android.transition.Transition removeTarget(android.view.View); + method public android.transition.Transition removeTarget(java.lang.Class); method public android.transition.Transition setDuration(long); method public void setEpicenterCallback(android.transition.Transition.EpicenterCallback); method public android.transition.Transition setInterpolator(android.animation.TimeInterpolator); diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 49a0138734bb..5a432dcc473b 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -24,6 +24,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.SparseLongArray; import android.view.SurfaceView; import android.view.TextureView; @@ -89,7 +90,8 @@ import java.util.List; * out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which * takes a set of {@link android.R.styleable#TransitionTarget target} tags, each * of which lists a specific <code>targetId</code>, <code>targetClass</code>, - * <code>excludeId</code>, or <code>excludeClass</code>, which this transition acts upon. + * <code>targetViewName</code>, <code>excludeId</code>, <code>excludeClass</code>, or + * <code>excludeViewName</code>, which this transition acts upon. * Use of targets is optional, but can be used to either limit the time spent checking * attributes on unchanging views, or limiting the types of animations run on specific views. * In this case, we know that only the <code>grayscaleContainer</code> will be @@ -113,10 +115,12 @@ public abstract class Transition implements Cloneable { TimeInterpolator mInterpolator = null; ArrayList<Integer> mTargetIds = new ArrayList<Integer>(); ArrayList<View> mTargets = new ArrayList<View>(); + ArrayList<String> mTargetNames = null; + ArrayList<Class> mTargetTypes = null; ArrayList<Integer> mTargetIdExcludes = null; ArrayList<View> mTargetExcludes = null; ArrayList<Class> mTargetTypeExcludes = null; - ArrayList<Class> mTargetTypes = null; + ArrayList<String> mTargetNameExcludes = null; ArrayList<Integer> mTargetIdChildExcludes = null; ArrayList<View> mTargetChildExcludes = null; ArrayList<Class> mTargetTypeChildExcludes = null; @@ -334,6 +338,133 @@ public abstract class Transition implements Cloneable { } /** + * Match start/end values by View instance. Adds matched values to startValuesList + * and endValuesList and removes them from unmatchedStart and unmatchedEnd. + */ + private void matchInstances(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd) { + for (int i = unmatchedStart.size() - 1; i >= 0; i--) { + View view = unmatchedStart.keyAt(i); + TransitionValues end = unmatchedEnd.remove(view); + if (end != null) { + TransitionValues start = unmatchedStart.removeAt(i); + startValuesList.add(start); + endValuesList.add(end); + } + } + } + + /** + * Match start/end values by Adapter item ID. Adds matched values to startValuesList + * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using + * startItemIds and endItemIds as a guide for which Views have unique item IDs. + */ + private void matchItemIds(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd, + LongSparseArray<View> startItemIds, LongSparseArray<View> endItemIds) { + int numStartIds = startItemIds.size(); + for (int i = 0; i < numStartIds; i++) { + View startView = startItemIds.valueAt(i); + if (startView != null) { + View endView = endItemIds.get(startItemIds.keyAt(i)); + if (endView != null) { + TransitionValues startValues = unmatchedStart.get(startView); + TransitionValues endValues = unmatchedEnd.get(endView); + if (startValues != null && endValues != null) { + startValuesList.add(startValues); + endValuesList.add(endValues); + unmatchedStart.remove(startView); + unmatchedEnd.remove(endView); + } + } + } + } + } + + /** + * Match start/end values by Adapter view ID. Adds matched values to startValuesList + * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using + * startIds and endIds as a guide for which Views have unique IDs. + */ + private void matchIds(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd, + SparseArray<View> startIds, SparseArray<View> endIds) { + int numStartIds = startIds.size(); + for (int i = 0; i < numStartIds; i++) { + View startView = startIds.valueAt(i); + if (startView != null && isValidTarget(startView)) { + View endView = endIds.get(startIds.keyAt(i)); + if (endView != null && isValidTarget(endView)) { + TransitionValues startValues = unmatchedStart.get(startView); + TransitionValues endValues = unmatchedEnd.get(endView); + if (startValues != null && endValues != null) { + startValuesList.add(startValues); + endValuesList.add(endValues); + unmatchedStart.remove(startView); + unmatchedEnd.remove(endView); + } + } + } + } + } + + /** + * Match start/end values by Adapter viewName. Adds matched values to startValuesList + * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using + * startNames and endNames as a guide for which Views have unique viewNames. + */ + private void matchNames(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd, + ArrayMap<String, View> startNames, ArrayMap<String, View> endNames) { + int numStartNames = startNames.size(); + for (int i = 0; i < numStartNames; i++) { + View startView = startNames.valueAt(i); + if (startView != null && isValidTarget(startView)) { + View endView = endNames.get(startNames.keyAt(i)); + if (endView != null && isValidTarget(endView)) { + TransitionValues startValues = unmatchedStart.get(startView); + TransitionValues endValues = unmatchedEnd.get(endView); + if (startValues != null && endValues != null) { + startValuesList.add(startValues); + endValuesList.add(endValues); + unmatchedStart.remove(startView); + unmatchedEnd.remove(endView); + } + } + } + } + } + + /** + * Adds all values from unmatchedStart and unmatchedEnd to startValuesList and endValuesList, + * assuming that there is no match between values in the list. + */ + private void addUnmatched(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd) { + // Views that only exist in the start Scene + for (int i = 0; i < unmatchedStart.size(); i++) { + startValuesList.add(unmatchedStart.valueAt(i)); + endValuesList.add(null); + } + + // Views that only exist in the end Scene + for (int i = 0; i < unmatchedEnd.size(); i++) { + endValuesList.add(unmatchedEnd.valueAt(i)); + startValuesList.add(null); + } + } + + /** * This method, essentially a wrapper around all calls to createAnimator for all * possible target views, is called with the entire set of start/end * values. The implementation in Transition iterates through these lists @@ -349,76 +480,22 @@ public abstract class Transition implements Cloneable { if (DBG) { Log.d(LOG_TAG, "createAnimators() for " + this); } - ArrayMap<View, TransitionValues> endCopy = + ArrayMap<View, TransitionValues> unmatchedStart = + new ArrayMap<View, TransitionValues>(startValues.viewValues); + ArrayMap<View, TransitionValues> unmatchedEnd = new ArrayMap<View, TransitionValues>(endValues.viewValues); - SparseArray<TransitionValues> endIdCopy = endValues.idValues.clone(); - LongSparseArray<TransitionValues> endItemIdCopy = endValues.itemIdValues.clone(); - // Walk through the start values, playing everything we find - // Remove from the end set as we go + ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>(); ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>(); - for (View view : startValues.viewValues.keySet()) { - TransitionValues start = null; - TransitionValues end = null; - boolean isInListView = false; - if (view.getParent() instanceof ListView) { - isInListView = true; - } - if (!isInListView) { - int id = view.getId(); - start = startValues.viewValues.get(view); - end = endValues.viewValues.get(view); - if (end != null) { - endCopy.remove(view); - } else if (id != View.NO_ID) { - end = endIdCopy.get(id); - if (end == null || startValues.viewValues.containsKey(end.view)) { - end = null; - id = View.NO_ID; - } else { - endCopy.remove(end.view); - } - } - endIdCopy.remove(id); - if (isValidTarget(view, id)) { - startValuesList.add(start); - endValuesList.add(end); - } - } else { - ListView parent = (ListView) view.getParent(); - if (parent.getAdapter().hasStableIds()) { - int position = parent.getPositionForView(view); - long itemId = parent.getItemIdAtPosition(position); - start = startValues.itemIdValues.get(itemId); - endItemIdCopy.remove(itemId); - // TODO: deal with targetIDs for itemIDs for ListView items - startValuesList.add(start); - endValuesList.add(end); - } - } - } - int startItemIdCopySize = startValues.itemIdValues.size(); - for (int i = 0; i < startItemIdCopySize; ++i) { - long id = startValues.itemIdValues.keyAt(i); - if (isValidTarget(null, id)) { - TransitionValues start = startValues.itemIdValues.get(id); - TransitionValues end = endValues.itemIdValues.get(id); - endItemIdCopy.remove(id); - startValuesList.add(start); - endValuesList.add(end); - } - } - // Now walk through the remains of the end set - // We've already matched everything from start to end, everything else doesn't match. - for (View view : endCopy.keySet()) { - int id = view.getId(); - if (isValidTarget(view, id)) { - TransitionValues start = null; - TransitionValues end = endCopy.get(view); - startValuesList.add(start); - endValuesList.add(end); - } - } + matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.nameValues, endValues.nameValues); + matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd); + matchIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.idValues, endValues.idValues); + matchItemIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.itemIdValues, endValues.itemIdValues); + addUnmatched(startValuesList, endValuesList, unmatchedStart, unmatchedEnd); + ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators(); long minStartDelay = Long.MAX_VALUE; int minAnimator = mAnimators.size(); @@ -520,7 +597,8 @@ public abstract class Transition implements Cloneable { * is not checked (this is in the case of ListView items, where the * views are ignored and only the ids are used). */ - boolean isValidTarget(View target, long targetId) { + boolean isValidTarget(View target) { + int targetId = target.getId(); if (mTargetIdExcludes != null && mTargetIdExcludes.contains(targetId)) { return false; } @@ -536,10 +614,20 @@ public abstract class Transition implements Cloneable { } } } - if (mTargetIds.size() == 0 && mTargets.size() == 0 && mTargetTypes == null) { + if (mTargetNameExcludes != null && target != null && target.getViewName() != null) { + if (mTargetNameExcludes.contains(target.getViewName())) { + return false; + } + } + if (mTargetIds.size() == 0 && mTargets.size() == 0 && + (mTargetTypes == null || mTargetTypes.isEmpty() && + (mTargetNames == null || mTargetNames.isEmpty()))) { + return true; + } + if (mTargetIds.contains(targetId) || mTargets.contains(target)) { return true; } - if (mTargetIds.contains((int) targetId) || mTargets.contains(target)) { + if (mTargetNames != null && mTargetNames.contains(target.getViewName())) { return true; } if (mTargetTypes != null) { @@ -690,6 +778,33 @@ public abstract class Transition implements Cloneable { } /** + * Adds the viewName of a target view that this Transition is interested in + * animating. By default, there are no targetNames, and a Transition will + * listen for changes on every view in the hierarchy below the sceneRoot + * of the Scene being transitioned into. Setting targetNames constrains + * the Transition to only listen for, and act on, views with these viewNames. + * Views with different viewNames, or no viewName whatsoever, will be ignored. + * + * <p>Note that viewNames should be unique within the view hierarchy.</p> + * + * @see android.view.View#getViewName() + * @param targetName The viewName of a target view, must be non-null. + * @return The Transition to which the target viewName is added. + * Returning the same object makes it easier to chain calls during + * construction, such as + * <code>transitionSet.addTransitions(new Fade()).addTarget(someName);</code> + */ + public Transition addTarget(String targetName) { + if (targetName != null) { + if (mTargetNames != null) { + mTargetNames = new ArrayList<String>(); + } + mTargetNames.add(targetName); + } + return this; + } + + /** * Adds the Class of a target view that this Transition is interested in * animating. By default, there are no targetTypes, and a Transition will * listen for changes on every view in the hierarchy below the sceneRoot @@ -712,10 +827,12 @@ public abstract class Transition implements Cloneable { * <code>transitionSet.addTransitions(new Fade()).addTarget(ImageView.class);</code> */ public Transition addTarget(Class targetType) { - if (mTargetTypes == null) { - mTargetTypes = new ArrayList<Class>(); + if (targetType != null) { + if (mTargetTypes == null) { + mTargetTypes = new ArrayList<Class>(); + } + mTargetTypes.add(targetType); } - mTargetTypes.add(targetType); return this; } @@ -737,6 +854,23 @@ public abstract class Transition implements Cloneable { } /** + * Removes the given targetName from the list of viewNames that this Transition + * is interested in animating. + * + * @param targetName The viewName of a target view, must not be null. + * @return The Transition from which the targetName is removed. + * Returning the same object makes it easier to chain calls during + * construction, such as + * <code>transitionSet.addTransitions(new Fade()).removeTargetName(someName);</code> + */ + public Transition removeTarget(String targetName) { + if (targetName != null && mTargetNames != null) { + mTargetNames.remove(targetName); + } + return this; + } + + /** * Whether to add the given id to the list of target ids to exclude from this * transition. The <code>exclude</code> parameter specifies whether the target * should be added to or removed from the excluded list. @@ -758,7 +892,35 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeTarget(int targetId, boolean exclude) { - mTargetIdExcludes = excludeId(mTargetIdExcludes, targetId, exclude); + if (targetId >= 0) { + mTargetIdExcludes = excludeObject(mTargetIdExcludes, targetId, exclude); + } + return this; + } + + /** + * Whether to add the given viewName to the list of target viewNames to exclude from this + * transition. The <code>exclude</code> parameter specifies whether the target + * should be added to or removed from the excluded list. + * + * <p>Excluding targets is a general mechanism for allowing transitions to run on + * a view hierarchy while skipping target views that should not be part of + * the transition. For example, you may want to avoid animating children + * of a specific ListView or Spinner. Views can be excluded by their + * id, their instance reference, their viewName, or by the Class of that view + * (eg, {@link Spinner}).</p> + * + * @see #excludeTarget(View, boolean) + * @see #excludeTarget(int, boolean) + * @see #excludeTarget(Class, boolean) + * + * @param targetViewName The name of a target to ignore when running this transition. + * @param exclude Whether to add the target to or remove the target from the + * current list of excluded targets. + * @return This transition object. + */ + public Transition excludeTarget(String targetViewName, boolean exclude) { + mTargetNameExcludes = excludeObject(mTargetNameExcludes, targetViewName, exclude); return this; } @@ -788,23 +950,10 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeChildren(int targetId, boolean exclude) { - mTargetIdChildExcludes = excludeId(mTargetIdChildExcludes, targetId, exclude); - return this; - } - - /** - * Utility method to manage the boilerplate code that is the same whether we - * are excluding targets or their children. - */ - private ArrayList<Integer> excludeId(ArrayList<Integer> list, int targetId, boolean exclude) { - if (targetId > 0) { - if (exclude) { - list = ArrayListManager.add(list, targetId); - } else { - list = ArrayListManager.remove(list, targetId); - } + if (targetId >= 0) { + mTargetIdChildExcludes = excludeObject(mTargetIdChildExcludes, targetId, exclude); } - return list; + return this; } /** @@ -829,7 +978,7 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeTarget(View target, boolean exclude) { - mTargetExcludes = excludeView(mTargetExcludes, target, exclude); + mTargetExcludes = excludeObject(mTargetExcludes, target, exclude); return this; } @@ -855,7 +1004,7 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeChildren(View target, boolean exclude) { - mTargetChildExcludes = excludeView(mTargetChildExcludes, target, exclude); + mTargetChildExcludes = excludeObject(mTargetChildExcludes, target, exclude); return this; } @@ -863,7 +1012,7 @@ public abstract class Transition implements Cloneable { * Utility method to manage the boilerplate code that is the same whether we * are excluding targets or their children. */ - private ArrayList<View> excludeView(ArrayList<View> list, View target, boolean exclude) { + private static <T> ArrayList<T> excludeObject(ArrayList<T> list, T target, boolean exclude) { if (target != null) { if (exclude) { list = ArrayListManager.add(list, target); @@ -896,7 +1045,7 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeTarget(Class type, boolean exclude) { - mTargetTypeExcludes = excludeType(mTargetTypeExcludes, type, exclude); + mTargetTypeExcludes = excludeObject(mTargetTypeExcludes, type, exclude); return this; } @@ -923,26 +1072,11 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeChildren(Class type, boolean exclude) { - mTargetTypeChildExcludes = excludeType(mTargetTypeChildExcludes, type, exclude); + mTargetTypeChildExcludes = excludeObject(mTargetTypeChildExcludes, type, exclude); return this; } /** - * Utility method to manage the boilerplate code that is the same whether we - * are excluding targets or their children. - */ - private ArrayList<Class> excludeType(ArrayList<Class> list, Class type, boolean exclude) { - if (type != null) { - if (exclude) { - list = ArrayListManager.add(list, type); - } else { - list = ArrayListManager.remove(list, type); - } - } - return list; - } - - /** * Sets the target view instances that this Transition is interested in * animating. By default, there are no targets, and a Transition will * listen for changes on every view in the hierarchy below the sceneRoot @@ -991,9 +1125,27 @@ public abstract class Transition implements Cloneable { } /** - * Returns the array of target IDs that this transition limits itself to - * tracking and animating. If the array is null for both this method and - * {@link #getTargets()}, then this transition is + * Removes the given target from the list of targets that this Transition + * is interested in animating. + * + * @param target The type of the target view, must be non-null. + * @return Transition The Transition from which the target is removed. + * Returning the same object makes it easier to chain calls during + * construction, such as + * <code>transitionSet.addTransitions(new Fade()).removeTarget(someType);</code> + */ + public Transition removeTarget(Class target) { + if (target != null) { + mTargetTypes.remove(target); + } + return this; + } + + /** + * Returns the list of target IDs that this transition limits itself to + * tracking and animating. If the list is null or empty for + * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and + * {@link #getTargetTypes()} then this transition is * not limited to specific views, and will handle changes to any views * in the hierarchy of a scene change. * @@ -1004,9 +1156,10 @@ public abstract class Transition implements Cloneable { } /** - * Returns the array of target views that this transition limits itself to - * tracking and animating. If the array is null for both this method and - * {@link #getTargetIds()}, then this transition is + * Returns the list of target views that this transition limits itself to + * tracking and animating. If the list is null or empty for + * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and + * {@link #getTargetTypes()} then this transition is * not limited to specific views, and will handle changes to any views * in the hierarchy of a scene change. * @@ -1017,6 +1170,34 @@ public abstract class Transition implements Cloneable { } /** + * Returns the list of target viewNames that this transition limits itself to + * tracking and animating. If the list is null or empty for + * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and + * {@link #getTargetTypes()} then this transition is + * not limited to specific views, and will handle changes to any views + * in the hierarchy of a scene change. + * + * @return the list of target viewNames + */ + public List<String> getTargetViewNames() { + return mTargetNames; + } + + /** + * Returns the list of target viewNames that this transition limits itself to + * tracking and animating. If the list is null or empty for + * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and + * {@link #getTargetTypes()} then this transition is + * not limited to specific views, and will handle changes to any views + * in the hierarchy of a scene change. + * + * @return the list of target Types + */ + public List<Class> getTargetTypes() { + return mTargetTypes; + } + + /** * Recursive method that captures values for the given view and the * hierarchy underneath it. * @param sceneRoot The root of the view hierarchy being captured @@ -1025,52 +1206,42 @@ public abstract class Transition implements Cloneable { */ void captureValues(ViewGroup sceneRoot, boolean start) { clearValues(start); - if (mTargetIds.size() > 0 || mTargets.size() > 0) { - if (mTargetIds.size() > 0) { - for (int i = 0; i < mTargetIds.size(); ++i) { - int id = mTargetIds.get(i); - View view = sceneRoot.findViewById(id); - if (view != null) { - TransitionValues values = new TransitionValues(); - values.view = view; - if (start) { - captureStartValues(values); - } else { - captureEndValues(values); - } - capturePropagationValues(values); - if (start) { - mStartValues.viewValues.put(view, values); - if (id >= 0) { - mStartValues.idValues.put(id, values); - } - } else { - mEndValues.viewValues.put(view, values); - if (id >= 0) { - mEndValues.idValues.put(id, values); - } - } + if ((mTargetIds.size() > 0 || mTargets.size() > 0) + && (mTargetNames == null || mTargetNames.isEmpty()) + && (mTargetTypes == null || mTargetTypes.isEmpty())) { + for (int i = 0; i < mTargetIds.size(); ++i) { + int id = mTargetIds.get(i); + View view = sceneRoot.findViewById(id); + if (view != null) { + TransitionValues values = new TransitionValues(); + values.view = view; + if (start) { + captureStartValues(values); + } else { + captureEndValues(values); + } + capturePropagationValues(values); + if (start) { + addViewValues(mStartValues, view, values); + } else { + addViewValues(mEndValues, view, values); } } } - if (mTargets.size() > 0) { - for (int i = 0; i < mTargets.size(); ++i) { - View view = mTargets.get(i); - if (view != null) { - TransitionValues values = new TransitionValues(); - values.view = view; - if (start) { - captureStartValues(values); - } else { - captureEndValues(values); - } - capturePropagationValues(values); - if (start) { - mStartValues.viewValues.put(view, values); - } else { - mEndValues.viewValues.put(view, values); - } - } + for (int i = 0; i < mTargets.size(); ++i) { + View view = mTargets.get(i); + TransitionValues values = new TransitionValues(); + values.view = view; + if (start) { + captureStartValues(values); + } else { + captureEndValues(values); + } + capturePropagationValues(values); + if (start) { + mStartValues.viewValues.put(view, values); + } else { + mEndValues.viewValues.put(view, values); } } } else { @@ -1078,6 +1249,47 @@ public abstract class Transition implements Cloneable { } } + static void addViewValues(TransitionValuesMaps transitionValuesMaps, + View view, TransitionValues transitionValues) { + transitionValuesMaps.viewValues.put(view, transitionValues); + int id = view.getId(); + if (id >= 0) { + if (transitionValuesMaps.idValues.indexOfKey(id) >= 0) { + // Duplicate IDs cannot match by ID. + transitionValuesMaps.idValues.put(id, null); + } else { + transitionValuesMaps.idValues.put(id, view); + } + } + String name = view.getViewName(); + if (name != null) { + if (transitionValuesMaps.nameValues.containsKey(name)) { + // Duplicate viewNames: cannot match by viewName. + transitionValuesMaps.nameValues.put(name, null); + } else { + transitionValuesMaps.nameValues.put(name, view); + } + } + if (view.getParent() instanceof ListView) { + ListView listview = (ListView) view.getParent(); + if (listview.getAdapter().hasStableIds()) { + int position = listview.getPositionForView(view); + long itemId = listview.getItemIdAtPosition(position); + if (transitionValuesMaps.itemIdValues.indexOfKey(itemId) >= 0) { + // Duplicate item IDs: cannot match by item ID. + View alreadyMatched = transitionValuesMaps.itemIdValues.get(itemId); + if (alreadyMatched != null) { + alreadyMatched.setHasTransientState(false); + transitionValuesMaps.itemIdValues.put(itemId, null); + } + } else { + view.setHasTransientState(true); + transitionValuesMaps.itemIdValues.put(itemId, view); + } + } + } + } + /** * Clear valuesMaps for specified start/end state * @@ -1109,24 +1321,7 @@ public abstract class Transition implements Cloneable { if (view == null) { return; } - boolean isListViewItem = false; - if (view.getParent() instanceof ListView) { - isListViewItem = true; - } - if (isListViewItem && !((ListView) view.getParent()).getAdapter().hasStableIds()) { - // ignore listview children unless we can track them with stable IDs - return; - } - int id = View.NO_ID; - long itemId = View.NO_ID; - if (!isListViewItem) { - id = view.getId(); - } else { - ListView listview = (ListView) view.getParent(); - int position = listview.getPositionForView(view); - itemId = listview.getItemIdAtPosition(position); - view.setHasTransientState(true); - } + int id = view.getId(); if (mTargetIdExcludes != null && mTargetIdExcludes.contains(id)) { return; } @@ -1151,23 +1346,9 @@ public abstract class Transition implements Cloneable { } capturePropagationValues(values); if (start) { - if (!isListViewItem) { - mStartValues.viewValues.put(view, values); - if (id >= 0) { - mStartValues.idValues.put((int) id, values); - } - } else { - mStartValues.itemIdValues.put(itemId, values); - } + addViewValues(mStartValues, view, values); } else { - if (!isListViewItem) { - mEndValues.viewValues.put(view, values); - if (id >= 0) { - mEndValues.idValues.put((int) id, values); - } - } else { - mEndValues.itemIdValues.put(itemId, values); - } + addViewValues(mEndValues, view, values); } } if (view instanceof ViewGroup) { @@ -1178,7 +1359,7 @@ public abstract class Transition implements Cloneable { if (mTargetChildExcludes != null && mTargetChildExcludes.contains(view)) { return; } - if (mTargetTypeChildExcludes != null && view != null) { + if (mTargetTypeChildExcludes != null) { int numTypes = mTargetTypeChildExcludes.size(); for (int i = 0; i < numTypes; ++i) { if (mTargetTypeChildExcludes.get(i).isInstance(view)) { @@ -1204,22 +1385,7 @@ public abstract class Transition implements Cloneable { return mParent.getTransitionValues(view, start); } TransitionValuesMaps valuesMaps = start ? mStartValues : mEndValues; - TransitionValues values = valuesMaps.viewValues.get(view); - if (values == null) { - int id = view.getId(); - if (id >= 0) { - values = valuesMaps.idValues.get(id); - } - if (values == null && view.getParent() instanceof ListView) { - ListView listview = (ListView) view.getParent(); - int position = listview.getPositionForView(view); - long itemId = listview.getItemIdAtPosition(position); - values = valuesMaps.itemIdValues.get(itemId); - } - // TODO: Doesn't handle the case where a view was parented to a - // ListView (with an itemId), but no longer is - } - return values; + return valuesMaps.viewValues.get(view); } /** @@ -1303,11 +1469,7 @@ public abstract class Transition implements Cloneable { boolean cancel = false; TransitionValues oldValues = oldInfo.values; View oldView = oldInfo.view; - TransitionValues newValues = mEndValues.viewValues != null ? - mEndValues.viewValues.get(oldView) : null; - if (newValues == null) { - newValues = mEndValues.idValues.get(oldView.getId()); - } + TransitionValues newValues = mEndValues.viewValues.get(oldView); if (oldValues != null) { // if oldValues null, then transition didn't care to stash values, // and won't get canceled @@ -1429,17 +1591,15 @@ public abstract class Transition implements Cloneable { } } for (int i = 0; i < mStartValues.itemIdValues.size(); ++i) { - TransitionValues tv = mStartValues.itemIdValues.valueAt(i); - View v = tv.view; - if (v.hasTransientState()) { - v.setHasTransientState(false); + View view = mStartValues.itemIdValues.valueAt(i); + if (view != null) { + view.setHasTransientState(false); } } for (int i = 0; i < mEndValues.itemIdValues.size(); ++i) { - TransitionValues tv = mEndValues.itemIdValues.valueAt(i); - View v = tv.view; - if (v.hasTransientState()) { - v.setHasTransientState(false); + View view = mEndValues.itemIdValues.valueAt(i); + if (view != null) { + view.setHasTransientState(false); } } mEnded = true; diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java index a5e960aa4d9f..04f8672f83aa 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -229,11 +229,20 @@ public class TransitionInflater { com.android.internal.R.styleable.TransitionTarget); int id = a.getResourceId( com.android.internal.R.styleable.TransitionTarget_targetId, -1); + String viewName; if (id >= 0) { transition.addTarget(id); } else if ((id = a.getResourceId( com.android.internal.R.styleable.TransitionTarget_excludeId, -1)) >= 0) { transition.excludeTarget(id, true); + } else if ((viewName = a.getString( + com.android.internal.R.styleable.TransitionTarget_targetViewName)) + != null) { + transition.addTarget(viewName); + } else if ((viewName = a.getString( + com.android.internal.R.styleable.TransitionTarget_excludeViewName)) + != null) { + transition.excludeTarget(viewName, true); } else { String className = a.getString( com.android.internal.R.styleable.TransitionTarget_excludeClass); diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index 90812342e425..698b5638795a 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -272,24 +272,8 @@ public class TransitionSet extends Transition { int numValues = values.viewValues.size(); for (int i = 0; i < numValues; i++) { View view = values.viewValues.keyAt(i); - if (isValidTarget(view, view.getId())) { - included.viewValues.put(view, values.viewValues.valueAt(i)); - } - } - numValues = values.idValues.size(); - for (int i = 0; i < numValues; i++) { - int id = values.idValues.keyAt(i); - TransitionValues transitionValues = values.idValues.valueAt(i); - if (isValidTarget(transitionValues.view, id)) { - included.idValues.put(id, transitionValues); - } - } - numValues = values.itemIdValues.size(); - for (int i = 0; i < numValues; i++) { - long id = values.itemIdValues.keyAt(i); - TransitionValues transitionValues = values.itemIdValues.valueAt(i); - if (isValidTarget(transitionValues.view, id)) { - included.itemIdValues.put(id, transitionValues); + if (isValidTarget(view)) { + addViewValues(included, view, values.viewValues.valueAt(i)); } } return included; @@ -328,10 +312,9 @@ public class TransitionSet extends Transition { @Override public void captureStartValues(TransitionValues transitionValues) { - int targetId = transitionValues.view.getId(); - if (isValidTarget(transitionValues.view, targetId)) { + if (isValidTarget(transitionValues.view)) { for (Transition childTransition : mTransitions) { - if (childTransition.isValidTarget(transitionValues.view, targetId)) { + if (childTransition.isValidTarget(transitionValues.view)) { childTransition.captureStartValues(transitionValues); } } @@ -340,10 +323,9 @@ public class TransitionSet extends Transition { @Override public void captureEndValues(TransitionValues transitionValues) { - int targetId = transitionValues.view.getId(); - if (isValidTarget(transitionValues.view, targetId)) { + if (isValidTarget(transitionValues.view)) { for (Transition childTransition : mTransitions) { - if (childTransition.isValidTarget(transitionValues.view, targetId)) { + if (childTransition.isValidTarget(transitionValues.view)) { childTransition.captureEndValues(transitionValues); } } diff --git a/core/java/android/transition/TransitionValuesMaps.java b/core/java/android/transition/TransitionValuesMaps.java index 131596b97a5a..6d5700aca7a3 100644 --- a/core/java/android/transition/TransitionValuesMaps.java +++ b/core/java/android/transition/TransitionValuesMaps.java @@ -24,7 +24,7 @@ import android.view.View; class TransitionValuesMaps { ArrayMap<View, TransitionValues> viewValues = new ArrayMap<View, TransitionValues>(); - SparseArray<TransitionValues> idValues = new SparseArray<TransitionValues>(); - LongSparseArray<TransitionValues> itemIdValues = - new LongSparseArray<TransitionValues>(); + SparseArray<View> idValues = new SparseArray<View>(); + LongSparseArray<View> itemIdValues = new LongSparseArray<View>(); + ArrayMap<String, View> nameValues = new ArrayMap<String, View>(); } diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index 6e6496cd1431..0f7638b2e337 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -162,26 +162,15 @@ public abstract class Visibility extends Transition { public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues); - if (visInfo.visibilityChange) { - // Only transition views that are either targets of this transition - // or whose parent hierarchies remain stable between scenes - boolean isTarget = false; - if (mTargets.size() > 0 || mTargetIds.size() > 0) { - View startView = startValues != null ? startValues.view : null; - View endView = endValues != null ? endValues.view : null; - int startId = startView != null ? startView.getId() : -1; - int endId = endView != null ? endView.getId() : -1; - isTarget = isValidTarget(startView, startId) || isValidTarget(endView, endId); - } - if (isTarget || ((visInfo.startParent != null || visInfo.endParent != null))) { - if (visInfo.fadeIn) { - return onAppear(sceneRoot, startValues, visInfo.startVisibility, - endValues, visInfo.endVisibility); - } else { - return onDisappear(sceneRoot, startValues, visInfo.startVisibility, - endValues, visInfo.endVisibility - ); - } + if (visInfo.visibilityChange + && (visInfo.startParent != null || visInfo.endParent != null)) { + if (visInfo.fadeIn) { + return onAppear(sceneRoot, startValues, visInfo.startVisibility, + endValues, visInfo.endVisibility); + } else { + return onDisappear(sceneRoot, startValues, visInfo.startVisibility, + endValues, visInfo.endVisibility + ); } } return null; diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 0b83ad6b2e62..03435c5361df 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5020,10 +5020,14 @@ <attr name="targetId" format="reference" /> <!-- The id of a target to exclude from this transition. --> <attr name="excludeId" format="reference" /> - <!-- The fully-qualified name of the Class to exclude from this transition. --> - <attr name="excludeClass" format="string" /> <!-- The fully-qualified name of the Class to include in this transition. --> <attr name="targetClass" /> + <!-- The fully-qualified name of the Class to exclude from this transition. --> + <attr name="excludeClass" format="string" /> + <!-- The viewName of the target on which this transition will animation changes. --> + <attr name="targetViewName" format="string" /> + <!-- The viewName of the target to exclude from this transition. --> + <attr name="excludeViewName" format="string" /> </declare-styleable> <!-- Use <code>set</code> as the root tag of the XML resource that diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 1622abb53340..74b2f7b12d25 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2170,6 +2170,8 @@ <public type="attr" name="fromId" /> <public type="attr" name="reversible" /> <public type="attr" name="splitTrack" /> + <public type="attr" name="targetViewName" /> + <public type="attr" name="excludeViewName" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> |