Use safe access to OnPreDrawListener.
am: 61c1d4fa04
Change-Id: Ib6c4d142ab1adb6cdaa27764cf93511905342bdf
diff --git a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
index a8323dc..879be4a 100644
--- a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
+++ b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
@@ -24,7 +24,6 @@
import android.transition.TransitionSet;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import java.util.ArrayList;
import java.util.List;
@@ -343,18 +342,15 @@
}
}
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- for (int i = 0; i < numSharedElements; i++) {
- sharedElementsIn.get(i).setTransitionName(inNames.get(i));
- sharedElementsOut.get(i).setTransitionName(outNames.get(i));
- }
- return true;
- }
- });
+ OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < numSharedElements; i++) {
+ sharedElementsIn.get(i).setTransitionName(inNames.get(i));
+ sharedElementsOut.get(i).setTransitionName(outNames.get(i));
+ }
+ }
+ });
}
/**
@@ -406,23 +402,20 @@
public static void setNameOverridesUnoptimized(final View sceneRoot,
final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- final int numSharedElements = sharedElementsIn.size();
- for (int i = 0; i < numSharedElements; i++) {
- View view = sharedElementsIn.get(i);
- String name = view.getTransitionName();
- if (name != null) {
- String inName = findKeyForValue(nameOverrides, name);
- view.setTransitionName(inName);
- }
- }
- return true;
+ OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ final int numSharedElements = sharedElementsIn.size();
+ for (int i = 0; i < numSharedElements; i++) {
+ View view = sharedElementsIn.get(i);
+ String name = view.getTransitionName();
+ if (name != null) {
+ String inName = findKeyForValue(nameOverrides, name);
+ view.setTransitionName(inName);
}
- });
+ }
+ }
+ });
}
/**
@@ -566,20 +559,17 @@
public static void scheduleNameReset(final ViewGroup sceneRoot,
final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- final int numSharedElements = sharedElementsIn.size();
- for (int i = 0; i < numSharedElements; i++) {
- final View view = sharedElementsIn.get(i);
- final String name = view.getTransitionName();
- final String inName = nameOverrides.get(name);
- view.setTransitionName(inName);
- }
- return true;
- }
- });
+ OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ final int numSharedElements = sharedElementsIn.size();
+ for (int i = 0; i < numSharedElements; i++) {
+ final View view = sharedElementsIn.get(i);
+ final String name = view.getTransitionName();
+ final String inName = nameOverrides.get(name);
+ view.setTransitionName(inName);
+ }
+ }
+ });
}
}
diff --git a/fragment/api21/android/support/v4/app/OneShotPreDrawListener.java b/fragment/api21/android/support/v4/app/OneShotPreDrawListener.java
new file mode 100644
index 0000000..502af1f
--- /dev/null
+++ b/fragment/api21/android/support/v4/app/OneShotPreDrawListener.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+/**
+ * An OnPreDrawListener that will remove itself after one OnPreDraw call. Typical
+ * usage is:
+ * <pre><code>
+ * OneShotPreDrawListener.add(view, () -> { view.doSomething(); })
+ * </code></pre>
+ * <p>
+ * The onPreDraw always returns true.
+ * <p>
+ * The listener will also remove itself from the ViewTreeObserver when the view
+ * is detached from the view hierarchy. In that case, the Runnable will never be
+ * executed.
+ */
+class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListener,
+ View.OnAttachStateChangeListener {
+ private final View mView;
+ private ViewTreeObserver mViewTreeObserver;
+ private final Runnable mRunnable;
+
+ private OneShotPreDrawListener(View view, Runnable runnable) {
+ mView = view;
+ mViewTreeObserver = view.getViewTreeObserver();
+ mRunnable = runnable;
+ }
+
+ /**
+ * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver.
+ * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen.
+ * @param runnable The Runnable to execute in the OnPreDraw (once)
+ * @return The added OneShotPreDrawListener. It can be removed prior to
+ * the onPreDraw by calling {@link #removeListener()}.
+ */
+ public static OneShotPreDrawListener add(View view, Runnable runnable) {
+ OneShotPreDrawListener listener = new OneShotPreDrawListener(view, runnable);
+ view.getViewTreeObserver().addOnPreDrawListener(listener);
+ view.addOnAttachStateChangeListener(listener);
+ return listener;
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ removeListener();
+ mRunnable.run();
+ return true;
+ }
+
+ /**
+ * Removes the listener from the ViewTreeObserver. This is useful to call if the
+ * callback should be removed prior to {@link #onPreDraw()}.
+ */
+ public void removeListener() {
+ if (mViewTreeObserver.isAlive()) {
+ mViewTreeObserver.removeOnPreDrawListener(this);
+ } else {
+ mView.getViewTreeObserver().removeOnPreDrawListener(this);
+ }
+ mView.removeOnAttachStateChangeListener(this);
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mViewTreeObserver = v.getViewTreeObserver();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ removeListener();
+ }
+}
diff --git a/fragment/java/android/support/v4/app/FragmentTransition.java b/fragment/java/android/support/v4/app/FragmentTransition.java
index 9efd1bc..4676434 100644
--- a/fragment/java/android/support/v4/app/FragmentTransition.java
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -22,7 +22,6 @@
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import java.util.ArrayList;
import java.util.Collection;
@@ -250,15 +249,12 @@
FragmentTransitionCompat21.scheduleHideFragmentView(exitTransition,
exitingFragment.getView(), exitingViews);
final ViewGroup container = exitingFragment.mContainer;
- container.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- container.getViewTreeObserver().removeOnPreDrawListener(this);
- setViewVisibility(exitingViews, View.INVISIBLE);
- return true;
- }
- });
+ OneShotPreDrawListener.add(container, new Runnable() {
+ @Override
+ public void run() {
+ setViewVisibility(exitingViews, View.INVISIBLE);
+ }
+ });
}
}
@@ -356,33 +352,27 @@
final ArrayList<View> sharedElementsIn,
final Object enterTransition, final ArrayList<View> enteringViews,
final Object exitTransition, final ArrayList<View> exitingViews) {
+ OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ if (enterTransition != null) {
+ FragmentTransitionCompat21.removeTarget(enterTransition,
+ nonExistentView);
+ ArrayList<View> views = configureEnteringExitingViews(
+ enterTransition, inFragment, sharedElementsIn, nonExistentView);
+ enteringViews.addAll(views);
+ }
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-
- if (enterTransition != null) {
- FragmentTransitionCompat21.removeTarget(enterTransition,
- nonExistentView);
- ArrayList<View> views = configureEnteringExitingViews(
- enterTransition, inFragment, sharedElementsIn, nonExistentView);
- enteringViews.addAll(views);
- }
-
- if (exitingViews != null) {
- ArrayList<View> tempExiting = new ArrayList<>();
- tempExiting.add(nonExistentView);
- FragmentTransitionCompat21.replaceTargets(exitTransition, exitingViews,
- tempExiting);
- exitingViews.clear();
- exitingViews.add(nonExistentView);
- }
-
- return true;
- }
- });
+ if (exitingViews != null) {
+ ArrayList<View> tempExiting = new ArrayList<>();
+ tempExiting.add(nonExistentView);
+ FragmentTransitionCompat21.replaceTargets(exitTransition, exitingViews,
+ tempExiting);
+ exitingViews.clear();
+ exitingViews.add(nonExistentView);
+ }
+ }
+ });
}
/**
@@ -519,19 +509,16 @@
epicenterView = null;
}
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- callSharedElementStartEnd(inFragment, outFragment, inIsPop,
- inSharedElements, false);
- if (epicenterView != null) {
- FragmentTransitionCompat21.getBoundsOnScreen(epicenterView, epicenter);
- }
- return true;
- }
- });
+ OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+ inSharedElements, false);
+ if (epicenterView != null) {
+ FragmentTransitionCompat21.getBoundsOnScreen(epicenterView, epicenter);
+ }
+ }
+ });
return sharedElementTransition;
}
@@ -612,38 +599,36 @@
inEpicenter = null;
}
+
final Object finalSharedElementTransition = sharedElementTransition;
+ OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ ArrayMap<String, View> inSharedElements = captureInSharedElements(
+ nameOverrides, finalSharedElementTransition, fragments);
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- ArrayMap<String, View> inSharedElements = captureInSharedElements(
- nameOverrides, finalSharedElementTransition, fragments);
+ if (inSharedElements != null) {
+ sharedElementsIn.addAll(inSharedElements.values());
+ sharedElementsIn.add(nonExistentView);
+ }
- if (inSharedElements != null) {
- sharedElementsIn.addAll(inSharedElements.values());
- sharedElementsIn.add(nonExistentView);
- }
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+ inSharedElements, false);
+ if (finalSharedElementTransition != null) {
+ FragmentTransitionCompat21.swapSharedElementTargets(
+ finalSharedElementTransition, sharedElementsOut,
+ sharedElementsIn);
- callSharedElementStartEnd(inFragment, outFragment, inIsPop,
- inSharedElements, false);
- if (finalSharedElementTransition != null) {
- FragmentTransitionCompat21.swapSharedElementTargets(
- finalSharedElementTransition, sharedElementsOut,
- sharedElementsIn);
-
- final View inEpicenterView = getInEpicenterView(inSharedElements,
- fragments, enterTransition, inIsPop);
- if (inEpicenterView != null) {
- FragmentTransitionCompat21.getBoundsOnScreen(inEpicenterView,
- inEpicenter);
- }
- }
- return true;
+ final View inEpicenterView = getInEpicenterView(inSharedElements,
+ fragments, enterTransition, inIsPop);
+ if (inEpicenterView != null) {
+ FragmentTransitionCompat21.getBoundsOnScreen(inEpicenterView,
+ inEpicenter);
}
- });
+ }
+ }
+ });
+
return sharedElementTransition;
}