/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package android.support.v17.leanback.widget; import android.view.View; import android.view.ViewGroup; import java.util.HashMap; import java.util.Map; /** * A Presenter is used to generate {@link View}s and bind Objects to them on * demand. It is closely related to the concept of an {@link * android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, but is * not position-based. The leanback framework implements the adapter concept using * {@link ObjectAdapter} which refers to a Presenter (or {@link PresenterSelector}) instance. * *

* Presenters should be stateless. Presenters typically extend {@link ViewHolder} to store all * necessary view state information, such as references to child views to be used when * binding to avoid expensive calls to {@link View#findViewById(int)}. *

* *

* A trivial Presenter that takes a string and renders it into a {@link * android.widget.TextView TextView}: * *

 * public class StringTextViewPresenter extends Presenter {
 *     // This class does not need a custom ViewHolder, since it does not use
 *     // a complex layout.
 *
 *     {@literal @}Override
 *     public ViewHolder onCreateViewHolder(ViewGroup parent) {
 *         return new ViewHolder(new TextView(parent.getContext()));
 *     }
 *
 *     {@literal @}Override
 *     public void onBindViewHolder(ViewHolder viewHolder, Object item) {
 *         String str = (String) item;
 *         TextView textView = (TextView) viewHolder.mView;
 *
 *         textView.setText(item);
 *     }
 *
 *     {@literal @}Override
 *     public void onUnbindViewHolder(ViewHolder viewHolder) {
 *         // Nothing to unbind for TextView, but if this viewHolder had
 *         // allocated bitmaps, they can be released here.
 *     }
 * }
 * 
* In addition to view creation and binding, Presenter allows dynamic interface (facet) to * be added: {@link #setFacet(Class, Object)}. Supported facets: *
  • {@link ItemAlignmentFacet} is used by {@link HorizontalGridView} and * {@link VerticalGridView} to customize child alignment. */ public abstract class Presenter implements FacetProvider { /** * ViewHolder can be subclassed and used to cache any view accessors needed * to improve binding performance (for example, results of findViewById) * without needing to subclass a View. */ public static class ViewHolder implements FacetProvider { public final View view; private Map mFacets; public ViewHolder(View view) { this.view = view; } @Override public final Object getFacet(Class facetClass) { if (mFacets == null) { return null; } return mFacets.get(facetClass); } /** * Sets dynamic implemented facet in addition to basic ViewHolder functions. * @param facetClass Facet classes to query, can be class of {@link ItemAlignmentFacet}. * @param facetImpl Facet implementation. */ public final void setFacet(Class facetClass, Object facetImpl) { if (mFacets == null) { mFacets = new HashMap(); } mFacets.put(facetClass, facetImpl); } } private Map mFacets; /** * Creates a new {@link View}. */ public abstract ViewHolder onCreateViewHolder(ViewGroup parent); /** * Binds a {@link View} to an item. */ public abstract void onBindViewHolder(ViewHolder viewHolder, Object item); /** * Unbinds a {@link View} from an item. Any expensive references may be * released here, and any fields that are not bound for every item should be * cleared here. */ public abstract void onUnbindViewHolder(ViewHolder viewHolder); /** * Called when a view created by this presenter has been attached to a window. * *

    This can be used as a reasonable signal that the view is about to be seen * by the user. If the adapter previously freed any resources in * {@link #onViewDetachedFromWindow(ViewHolder)} * those resources should be restored here.

    * * @param holder Holder of the view being attached */ public void onViewAttachedToWindow(ViewHolder holder) { } /** * Called when a view created by this presenter has been detached from its window. * *

    Becoming detached from the window is not necessarily a permanent condition; * the consumer of an presenter's views may choose to cache views offscreen while they * are not visible, attaching and detaching them as appropriate.

    * * Any view property animations should be cancelled here or the view may fail * to be recycled. * * @param holder Holder of the view being detached */ public void onViewDetachedFromWindow(ViewHolder holder) { // If there are view property animations running then RecyclerView won't recycle. cancelAnimationsRecursive(holder.view); } /** * Utility method for removing all running animations on a view. */ protected static void cancelAnimationsRecursive(View view) { if (view != null && view.hasTransientState()) { view.animate().cancel(); if (view instanceof ViewGroup) { final int count = ((ViewGroup) view).getChildCount(); for (int i = 0; view.hasTransientState() && i < count; i++) { cancelAnimationsRecursive(((ViewGroup) view).getChildAt(i)); } } } } /** * Called to set a click listener for the given view holder. * * The default implementation sets the click listener on the root view in the view holder. * If the root view isn't focusable this method should be overridden to set the listener * on the appropriate focusable child view(s). * * @param holder The view holder containing the view(s) on which the listener should be set. * @param listener The click listener to be set. */ public void setOnClickListener(ViewHolder holder, View.OnClickListener listener) { holder.view.setOnClickListener(listener); } @Override public final Object getFacet(Class facetClass) { if (mFacets == null) { return null; } return mFacets.get(facetClass); } /** * Sets dynamic implemented facet in addition to basic Presenter functions. * @param facetClass Facet classes to query, can be class of {@link ItemAlignmentFacet}. * @param facetImpl Facet implementation. */ public final void setFacet(Class facetClass, Object facetImpl) { if (mFacets == null) { mFacets = new HashMap(); } mFacets.put(facetClass, facetImpl); } }