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 /**
20  * A Presenter is used to generate {@link View}s and bind Objects to them on
21  * demand. It is closely related to concept of an {@link
22  * android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, but is
23  * not position-based.
24  *
25  * <p>
26  * A trivial Presenter that takes a string and renders it into a {@link
27  * android.widget.TextView TextView}:
28  *
29  * <pre class="prettyprint">
30  * public class StringTextViewPresenter extends Presenter {
31  *     // This class does not need a custom ViewHolder, since it does not use
32  *     // a complex layout.
33  *
34  *     {@literal @}Override
35  *     public ViewHolder onCreateViewHolder(ViewGroup parent) {
36  *         return new ViewHolder(new TextView(parent.getContext()));
37  *     }
38  *
39  *     {@literal @}Override
40  *     public void onBindViewHolder(ViewHolder viewHolder, Object item) {
41  *         String str = (String) item;
42  *         TextView textView = (TextView) viewHolder.mView;
43  *
44  *         textView.setText(item);
45  *     }
46  *
47  *     {@literal @}Override
48  *     public void onUnbindViewHolder(ViewHolder viewHolder) {
49  *         // Nothing to unbind for TextView, but if this viewHolder had
50  *         // allocated bitmaps, they can be released here.
51  *     }
52  * }
53  * </pre>
54  */
55 public abstract class Presenter {
56     /**
57      * ViewHolder can be subclassed and used to cache any view accessors needed
58      * to improve binding performance (for example, results of findViewById)
59      * without needing to subclass a View.
60      */
61     public static class ViewHolder {
62         public final View view;
63 
ViewHolder(View view)64         public ViewHolder(View view) {
65             this.view = view;
66         }
67     }
68 
69     /**
70      * Creates a new {@link View}.
71      */
onCreateViewHolder(ViewGroup parent)72     public abstract ViewHolder onCreateViewHolder(ViewGroup parent);
73 
74     /**
75      * Binds a {@link View} to an item.
76      */
onBindViewHolder(ViewHolder viewHolder, Object item)77     public abstract void onBindViewHolder(ViewHolder viewHolder, Object item);
78 
79     /**
80      * Unbinds a {@link View} from an item. Any expensive references may be
81      * released here, and any fields that are not bound for every item should be
82      * cleared here.
83      */
onUnbindViewHolder(ViewHolder viewHolder)84     public abstract void onUnbindViewHolder(ViewHolder viewHolder);
85 
86     /**
87      * Called when a view created by this presenter has been attached to a window.
88      *
89      * <p>This can be used as a reasonable signal that the view is about to be seen
90      * by the user. If the adapter previously freed any resources in
91      * {@link #onViewDetachedFromWindow(ViewHolder)}
92      * those resources should be restored here.</p>
93      *
94      * @param holder Holder of the view being attached
95      */
onViewAttachedToWindow(ViewHolder holder)96     public void onViewAttachedToWindow(ViewHolder holder) {
97     }
98 
99     /**
100      * Called when a view created by this presenter has been detached from its window.
101      *
102      * <p>Becoming detached from the window is not necessarily a permanent condition;
103      * the consumer of an presenter's views may choose to cache views offscreen while they
104      * are not visible, attaching and detaching them as appropriate.</p>
105      *
106      * Any view property animations should be cancelled here or the view may fail
107      * to be recycled.
108      *
109      * @param holder Holder of the view being detached
110      */
onViewDetachedFromWindow(ViewHolder holder)111     public void onViewDetachedFromWindow(ViewHolder holder) {
112         // If there are view property animations running then RecyclerView won't recycle.
113         cancelAnimationsRecursive(holder.view);
114     }
115 
116     /**
117      * Utility method for removing all running animations on a view.
118      */
cancelAnimationsRecursive(View view)119     protected static void cancelAnimationsRecursive(View view) {
120         if (view != null && view.hasTransientState()) {
121             view.animate().cancel();
122             if (view instanceof ViewGroup) {
123                 final int count = ((ViewGroup) view).getChildCount();
124                 for (int i = 0; view.hasTransientState() && i < count; i++) {
125                     cancelAnimationsRecursive(((ViewGroup) view).getChildAt(i));
126                 }
127             }
128         }
129     }
130 
131     /**
132      * Called to set a click listener for the given view holder.
133      *
134      * The default implementation sets the click listener on the root view in the view holder.
135      * If the root view isn't focusable this method should be overridden to set the listener
136      * on the appropriate focusable child view(s).
137      *
138      * @param holder The view holder containing the view(s) on which the listener should be set.
139      * @param listener The click listener to be set.
140      */
setOnClickListener(ViewHolder holder, View.OnClickListener listener)141     public void setOnClickListener(ViewHolder holder, View.OnClickListener listener) {
142         holder.view.setOnClickListener(listener);
143     }
144 }
145