Presenter.java revision 08c56822b71ab0aa0b9bb03e5fd45e28f6e358b8
1ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist/*
2ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * Copyright (C) 2014 The Android Open Source Project
3ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
4ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * in compliance with the License. You may obtain a copy of the License at
6ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
7ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * http://www.apache.org/licenses/LICENSE-2.0
8ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
9ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * Unless required by applicable law or agreed to in writing, software distributed under the License
10ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * or implied. See the License for the specific language governing permissions and limitations under
12ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * the License.
13ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist */
14ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpackage android.support.v17.leanback.widget;
15ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
16ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport android.view.View;
17ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport android.view.ViewGroup;
18ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
19ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.HashMap;
20ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Map;
21ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
22ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist/**
23ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * A Presenter is used to generate {@link View}s and bind Objects to them on
24ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * demand. It is closely related to the concept of an {@link
25ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, but is
26ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * not position-based.  The leanback framework implements the adapter concept using
27ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * {@link ObjectAdapter} which refers to a Presenter (or {@link PresenterSelector}) instance.
28ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
29ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * <p>
30ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * Presenters should be stateless.  Presenters typically extend {@link ViewHolder} to store all
31ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * necessary view state information, such as references to child views to be used when
32ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * binding to avoid expensive calls to {@link View#findViewById(int)}.
33ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * </p>
34ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
35ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * <p>
36ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * A trivial Presenter that takes a string and renders it into a {@link
37ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * android.widget.TextView TextView}:
38ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
39ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * <pre class="prettyprint">
40ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * public class StringTextViewPresenter extends Presenter {
41ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     // This class does not need a custom ViewHolder, since it does not use
42ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     // a complex layout.
43ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
44ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     {@literal @}Override
45ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     public ViewHolder onCreateViewHolder(ViewGroup parent) {
46ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *         return new ViewHolder(new TextView(parent.getContext()));
47ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     }
48ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
49ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     {@literal @}Override
50ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     public void onBindViewHolder(ViewHolder viewHolder, Object item) {
51ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *         String str = (String) item;
52ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *         TextView textView = (TextView) viewHolder.mView;
53ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
54ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *         textView.setText(item);
55ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     }
56ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *
57ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     {@literal @}Override
58ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     public void onUnbindViewHolder(ViewHolder viewHolder) {
59ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *         // Nothing to unbind for TextView, but if this viewHolder had
60ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *         // allocated bitmaps, they can be released here.
61ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist *     }
62ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * }
63ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * </pre>
64ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * In addition to view creation and binding, Presenter allows dynamic interface (facet) to
65ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * be added: {@link #setFacet(Class, Object)}.  Supported facets:
66ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * <li> {@link ItemAlignmentFacet} is used by {@link HorizontalGridView} and
67ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist * {@link VerticalGridView} to customize child alignment.
68ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist */
69ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpublic abstract class Presenter implements FacetProvider {
70ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    /**
71ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * ViewHolder can be subclassed and used to cache any view accessors needed
72ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * to improve binding performance (for example, results of findViewById)
73ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * without needing to subclass a View.
74ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     */
75ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public static class ViewHolder implements FacetProvider {
76ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public final View view;
77ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        private Map<Class, Object> mFacets;
78ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
79ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public ViewHolder(View view) {
80ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            this.view = view;
81ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
82ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
83ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        @Override
84ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public final Object getFacet(Class<?> facetClass) {
85ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (mFacets == null) {
86ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                return null;
87ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
88ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return mFacets.get(facetClass);
89ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
90ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
91ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        /**
92ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist         * Sets dynamic implemented facet in addition to basic ViewHolder functions.
93ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist         * @param facetClass   Facet classes to query,  can be class of {@link ItemAlignmentFacet}.
94ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist         * @param facetImpl  Facet implementation.
95ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist         */
96ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        public final void setFacet(Class<?> facetClass, Object facetImpl) {
97ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (mFacets == null) {
98ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                mFacets = new HashMap<Class, Object>();
99ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
100ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            mFacets.put(facetClass, facetImpl);
101ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
102ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
103ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
104ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    private Map<Class, Object> mFacets;
105ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
106ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    /**
107ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * Creates a new {@link View}.
108ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     */
109ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public abstract ViewHolder onCreateViewHolder(ViewGroup parent);
110ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
111ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    /**
112ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * Binds a {@link View} to an item.
113ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     */
114ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public abstract void onBindViewHolder(ViewHolder viewHolder, Object item);
115ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
116ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    /**
117ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * Unbinds a {@link View} from an item. Any expensive references may be
118ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * released here, and any fields that are not bound for every item should be
119ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * cleared here.
120ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     */
121ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public abstract void onUnbindViewHolder(ViewHolder viewHolder);
122ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
123ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    /**
124ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * Called when a view created by this presenter has been attached to a window.
125ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     *
126ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * <p>This can be used as a reasonable signal that the view is about to be seen
127ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * by the user. If the adapter previously freed any resources in
128ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * {@link #onViewDetachedFromWindow(ViewHolder)}
129ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * those resources should be restored here.</p>
130ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     *
131ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * @param holder Holder of the view being attached
132ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     */
133ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public void onViewAttachedToWindow(ViewHolder holder) {
134ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
135ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
136ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    /**
137ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * Called when a view created by this presenter has been detached from its window.
138ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     *
139ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * <p>Becoming detached from the window is not necessarily a permanent condition;
140ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * the consumer of an presenter's views may choose to cache views offscreen while they
141ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * are not visible, attaching and detaching them as appropriate.</p>
142ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     *
143ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * Any view property animations should be cancelled here or the view may fail
144ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * to be recycled.
145ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     *
146ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * @param holder Holder of the view being detached
147ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     */
148ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public void onViewDetachedFromWindow(ViewHolder holder) {
149ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        // If there are view property animations running then RecyclerView won't recycle.
150ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        cancelAnimationsRecursive(holder.view);
151ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
152ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
153ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    /**
154ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * Utility method for removing all running animations on a view.
155ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     */
156ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    protected static void cancelAnimationsRecursive(View view) {
157ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        if (view != null && view.hasTransientState()) {
158ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            view.animate().cancel();
159ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            if (view instanceof ViewGroup) {
160ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                final int count = ((ViewGroup) view).getChildCount();
161ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                for (int i = 0; view.hasTransientState() && i < count; i++) {
162ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                    cancelAnimationsRecursive(((ViewGroup) view).getChildAt(i));
163ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist                }
164ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            }
165ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        }
166ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
167ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
168ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    /**
169ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * Called to set a click listener for the given view holder.
170ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     *
171ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * The default implementation sets the click listener on the root view in the view holder.
172ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * If the root view isn't focusable this method should be overridden to set the listener
173ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * on the appropriate focusable child view(s).
174ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     *
175ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * @param holder The view holder containing the view(s) on which the listener should be set.
176ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     * @param listener The click listener to be set.
177ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist     */
178ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public void setOnClickListener(ViewHolder holder, View.OnClickListener listener) {
179ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        holder.view.setOnClickListener(listener);
180ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    }
181ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist
182ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    @Override
183ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist    public final Object getFacet(Class<?> facetClass) {
184ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist        if (mFacets == null) {
185ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist            return null;
186        }
187        return mFacets.get(facetClass);
188    }
189
190    /**
191     * Sets dynamic implemented facet in addition to basic Presenter functions.
192     * @param facetClass   Facet classes to query,  can be class of {@link ItemAlignmentFacet}.
193     * @param facetImpl  Facet implementation.
194     */
195    public final void setFacet(Class<?> facetClass, Object facetImpl) {
196        if (mFacets == null) {
197            mFacets = new HashMap<Class, Object>();
198        }
199        mFacets.put(facetClass, facetImpl);
200    }
201}
202