1/*
2 * Copyright (C) 2016 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 android.support.v4.app;
17
18
19import android.view.View;
20import android.view.ViewTreeObserver;
21
22/**
23 * An OnPreDrawListener that will remove itself after one OnPreDraw call. Typical
24 * usage is:
25 * <pre><code>
26 *     OneShotPreDrawListener.add(view, () -> { view.doSomething(); })
27 * </code></pre>
28 * <p>
29 * The onPreDraw always returns true.
30 * <p>
31 * The listener will also remove itself from the ViewTreeObserver when the view
32 * is detached from the view hierarchy. In that case, the Runnable will never be
33 * executed.
34 */
35class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListener,
36        View.OnAttachStateChangeListener {
37    private final View mView;
38    private ViewTreeObserver mViewTreeObserver;
39    private final Runnable mRunnable;
40
41    private OneShotPreDrawListener(View view, Runnable runnable) {
42        mView = view;
43        mViewTreeObserver = view.getViewTreeObserver();
44        mRunnable = runnable;
45    }
46
47    /**
48     * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver.
49     * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen.
50     * @param runnable The Runnable to execute in the OnPreDraw (once)
51     * @return The added OneShotPreDrawListener. It can be removed prior to
52     * the onPreDraw by calling {@link #removeListener()}.
53     */
54    public static OneShotPreDrawListener add(View view, Runnable runnable) {
55        OneShotPreDrawListener listener = new OneShotPreDrawListener(view, runnable);
56        view.getViewTreeObserver().addOnPreDrawListener(listener);
57        view.addOnAttachStateChangeListener(listener);
58        return listener;
59    }
60
61    @Override
62    public boolean onPreDraw() {
63        removeListener();
64        mRunnable.run();
65        return true;
66    }
67
68    /**
69     * Removes the listener from the ViewTreeObserver. This is useful to call if the
70     * callback should be removed prior to {@link #onPreDraw()}.
71     */
72    public void removeListener() {
73        if (mViewTreeObserver.isAlive()) {
74            mViewTreeObserver.removeOnPreDrawListener(this);
75        } else {
76            mView.getViewTreeObserver().removeOnPreDrawListener(this);
77        }
78        mView.removeOnAttachStateChangeListener(this);
79    }
80
81    @Override
82    public void onViewAttachedToWindow(View v) {
83        mViewTreeObserver = v.getViewTreeObserver();
84    }
85
86    @Override
87    public void onViewDetachedFromWindow(View v) {
88        removeListener();
89    }
90}
91