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