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 */ 16 package com.android.internal.view; 17 18 import android.annotation.NonNull; 19 import android.view.View; 20 import 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 listener will also remove itself from the ViewTreeObserver when the view 30 * is detached from the view hierarchy. In that case, the Runnable will never be 31 * executed. 32 */ 33 public class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListener, 34 View.OnAttachStateChangeListener { 35 private final View mView; 36 private ViewTreeObserver mViewTreeObserver; 37 private final Runnable mRunnable; 38 private final boolean mReturnValue; 39 OneShotPreDrawListener(@onNull View view, boolean returnValue, @NonNull Runnable runnable)40 private OneShotPreDrawListener(@NonNull View view, boolean returnValue, 41 @NonNull Runnable runnable) { 42 mView = view; 43 mViewTreeObserver = view.getViewTreeObserver(); 44 mRunnable = runnable; 45 mReturnValue = returnValue; 46 } 47 48 /** 49 * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver. The 50 * return value from the OnPreDrawListener is {@code true}. 51 * 52 * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen. 53 * @param runnable The Runnable to execute in the OnPreDraw (once) 54 * @return The added OneShotPreDrawListener. It can be removed prior to 55 * the onPreDraw by calling {@link #removeListener()}. 56 */ add(@onNull View view, @NonNull Runnable runnable)57 public static OneShotPreDrawListener add(@NonNull View view, @NonNull Runnable runnable) { 58 return add(view, true, runnable); 59 } 60 61 /** 62 * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver. 63 * 64 * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen. 65 * @param returnValue The value to be returned from the OnPreDrawListener. 66 * @param runnable The Runnable to execute in the OnPreDraw (once) 67 * @return The added OneShotPreDrawListener. It can be removed prior to 68 * the onPreDraw by calling {@link #removeListener()}. 69 */ add(@onNull View view, boolean returnValue, @NonNull Runnable runnable)70 public static OneShotPreDrawListener add(@NonNull View view, boolean returnValue, 71 @NonNull Runnable runnable) { 72 OneShotPreDrawListener listener = new OneShotPreDrawListener(view, returnValue, runnable); 73 view.getViewTreeObserver().addOnPreDrawListener(listener); 74 view.addOnAttachStateChangeListener(listener); 75 return listener; 76 } 77 78 @Override onPreDraw()79 public boolean onPreDraw() { 80 removeListener(); 81 mRunnable.run(); 82 return mReturnValue; 83 } 84 85 /** 86 * Removes the listener from the ViewTreeObserver. This is useful to call if the 87 * callback should be removed prior to {@link #onPreDraw()}. 88 */ removeListener()89 public void removeListener() { 90 if (mViewTreeObserver.isAlive()) { 91 mViewTreeObserver.removeOnPreDrawListener(this); 92 } else { 93 mView.getViewTreeObserver().removeOnPreDrawListener(this); 94 } 95 mView.removeOnAttachStateChangeListener(this); 96 } 97 98 @Override onViewAttachedToWindow(@onNull View v)99 public void onViewAttachedToWindow(@NonNull View v) { 100 mViewTreeObserver = v.getViewTreeObserver(); 101 } 102 103 @Override onViewDetachedFromWindow(@onNull View v)104 public void onViewDetachedFromWindow(@NonNull View v) { 105 removeListener(); 106 } 107 } 108