ListenerUtil.java revision 716ba89e7f459f49ea85070d4710c1d79d715298
1/*
2 * Copyright (C) 2015 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.databinding.adapters;
17
18import android.os.Build.VERSION;
19import android.os.Build.VERSION_CODES;
20import android.util.SparseArray;
21import android.view.View;
22
23import java.lang.ref.WeakReference;
24import java.util.WeakHashMap;
25
26public class ListenerUtil {
27    private static SparseArray<WeakHashMap<View, WeakReference<?>>> sListeners =
28            new SparseArray<>();
29
30    /**
31     * This method tracks listeners for a View. Only one listener per listenerResourceId
32     * can be tracked at a time. This is useful for add*Listener and remove*Listener methods
33     * when used with BindingAdapters. This guarantees not to leak the listener or the View,
34     * so will not keep a strong reference to either.
35     *
36     * Example usage:
37     * <pre>
38     * {@code
39     * @BindingAdapter("onFoo")
40     * public static void addFooListener(MyView view, OnFooListener listener) {
41     *     OnFooListener oldValue = ListenerUtil.trackListener(view, listener, R.id.fooListener);
42     *     if (oldValue != null) {
43     *         view.removeOnFooListener(oldValue);
44     *     }
45     *     if (listener != null) {
46     *         view.addOnFooListener(listener);
47     *     }
48     * }
49     * }
50     * </pre>
51     *
52     * @param view The View that will have this listener
53     * @param listener The listener to keep track of. May be null if the listener is being removed.
54     * @param listenerResourceId A unique resource ID associated with the listener type.
55     * @return The previously tracked listener. This will be null if the View did not have
56     * a previously-tracked listener.
57     */
58    @SuppressWarnings("unchecked")
59    public static <T> T trackListener(View view, T listener, int listenerResourceId) {
60        if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
61            final T oldValue = (T) view.getTag(listenerResourceId);
62            view.setTag(listenerResourceId, listener);
63            return oldValue;
64        } else {
65            synchronized (sListeners) {
66                WeakHashMap<View, WeakReference<?>> listeners = sListeners.get(listenerResourceId);
67                if (listeners == null) {
68                    listeners = new WeakHashMap<>();
69                    sListeners.put(listenerResourceId, listeners);
70                }
71                final WeakReference<T> oldValue;
72                if (listener == null) {
73                    oldValue = (WeakReference<T>) listeners.remove(view);
74                } else {
75                    oldValue = (WeakReference<T>) listeners.put(view, new WeakReference(listener));
76                }
77                if (oldValue == null) {
78                    return null;
79                } else {
80                    return oldValue.get();
81                }
82            }
83        }
84    }
85
86}
87