120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn/*
220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * Copyright (C) 2014 The Android Open Source Project
320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * in compliance with the License. You may obtain a copy of the License at
620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * http://www.apache.org/licenses/LICENSE-2.0
820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * Unless required by applicable law or agreed to in writing, software distributed under the License
1020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
1120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * or implied. See the License for the specific language governing permissions and limitations under
1220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * the License.
1320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn */
1420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournpackage android.support.v17.leanback.widget;
1520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
1620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournimport android.view.View;
1720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournimport android.view.ViewGroup;
1820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
1920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn/**
2020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * A Presenter is used to generate {@link View}s and bind Objects to them on
2160d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn * demand. It is closely related to concept of an {@link
2260d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn * android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, but is
2360d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn * not position-based.
2420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
2520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * <p>
2620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * A trivial Presenter that takes a string and renders it into a {@link
2760d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn * android.widget.TextView TextView}:
2820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
2920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * <pre class="prettyprint">
3020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * public class StringTextViewPresenter extends Presenter {
3120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *     // This class does not need a custom ViewHolder, since it does not use
3220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *     // a complex layout.
3320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
3420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *     {@literal @}Override
3560d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn *     public ViewHolder onCreateViewHolder(ViewGroup parent) {
3620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *         return new ViewHolder(new TextView(parent.getContext()));
3720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *     }
3820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
3920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *     {@literal @}Override
4060d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn *     public void onBindViewHolder(ViewHolder viewHolder, Object item) {
4120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *         String str = (String) item;
4220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *         TextView textView = (TextView) viewHolder.mView;
4320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
4420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *         textView.setText(item);
4520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *     }
4620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
4720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *     {@literal @}Override
4860d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn *     public void onUnbindViewHolder(ViewHolder viewHolder) {
4960d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn *         // Nothing to unbind for TextView, but if this viewHolder had
5060d34fdccbe92a77e01b2cc63ffcab092a91009fTim Kilbourn *         // allocated bitmaps, they can be released here.
5120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *     }
5220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * }
5320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * </pre>
5420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn */
5520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournpublic abstract class Presenter {
5620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
5720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * ViewHolder can be subclassed and used to cache any view accessors needed
5820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * to improve binding performance (for example, results of findViewById)
5920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * without needing to subclass a View.
6020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
6120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public static class ViewHolder {
6220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        public final View view;
6320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
6420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        public ViewHolder(View view) {
6520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            this.view = view;
6620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        }
6720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
6820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
6920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
7020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Creates a new {@link View}.
7120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
7220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public abstract ViewHolder onCreateViewHolder(ViewGroup parent);
7320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
7420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
7520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Binds a {@link View} to an item.
7620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
7720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public abstract void onBindViewHolder(ViewHolder viewHolder, Object item);
7820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
7920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
8020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Unbinds a {@link View} from an item. Any expensive references may be
8120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * released here, and any fields that are not bound for every item should be
8220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * cleared here.
8320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
8420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public abstract void onUnbindViewHolder(ViewHolder viewHolder);
8547520b68e50572a9775a662410c5aff8300c8784Craig Stout
8647520b68e50572a9775a662410c5aff8300c8784Craig Stout    /**
8747520b68e50572a9775a662410c5aff8300c8784Craig Stout     * Called when a view created by this presenter has been attached to a window.
8847520b68e50572a9775a662410c5aff8300c8784Craig Stout     *
8947520b68e50572a9775a662410c5aff8300c8784Craig Stout     * <p>This can be used as a reasonable signal that the view is about to be seen
9047520b68e50572a9775a662410c5aff8300c8784Craig Stout     * by the user. If the adapter previously freed any resources in
9147520b68e50572a9775a662410c5aff8300c8784Craig Stout     * {@link #onViewDetachedFromWindow(ViewHolder)}
9247520b68e50572a9775a662410c5aff8300c8784Craig Stout     * those resources should be restored here.</p>
9347520b68e50572a9775a662410c5aff8300c8784Craig Stout     *
9447520b68e50572a9775a662410c5aff8300c8784Craig Stout     * @param holder Holder of the view being attached
9547520b68e50572a9775a662410c5aff8300c8784Craig Stout     */
9647520b68e50572a9775a662410c5aff8300c8784Craig Stout    public void onViewAttachedToWindow(ViewHolder holder) {
9747520b68e50572a9775a662410c5aff8300c8784Craig Stout    }
9847520b68e50572a9775a662410c5aff8300c8784Craig Stout
9947520b68e50572a9775a662410c5aff8300c8784Craig Stout    /**
10047520b68e50572a9775a662410c5aff8300c8784Craig Stout     * Called when a view created by this presenter has been detached from its window.
10147520b68e50572a9775a662410c5aff8300c8784Craig Stout     *
10247520b68e50572a9775a662410c5aff8300c8784Craig Stout     * <p>Becoming detached from the window is not necessarily a permanent condition;
10347520b68e50572a9775a662410c5aff8300c8784Craig Stout     * the consumer of an presenter's views may choose to cache views offscreen while they
10447520b68e50572a9775a662410c5aff8300c8784Craig Stout     * are not visible, attaching an detaching them as appropriate.</p>
10547520b68e50572a9775a662410c5aff8300c8784Craig Stout     *
1065358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout     * Any view property animations should be cancelled here or the view may fail
1075358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout     * to be recycled.
1085358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout     *
10947520b68e50572a9775a662410c5aff8300c8784Craig Stout     * @param holder Holder of the view being detached
11047520b68e50572a9775a662410c5aff8300c8784Craig Stout     */
11147520b68e50572a9775a662410c5aff8300c8784Craig Stout    public void onViewDetachedFromWindow(ViewHolder holder) {
1125358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout        // If there are view property animations running then RecyclerView won't recycle.
1135358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout        cancelAnimationsRecursive(holder.view);
1145358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout    }
1155358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout
1165358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout    /**
1175358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout     * Utility method for removing all running animations on a view.
1185358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout     */
1195358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout    protected static void cancelAnimationsRecursive(View view) {
1205358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout        if (view.hasTransientState()) {
1215358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout            view.animate().cancel();
1225358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout            if (view instanceof ViewGroup) {
1235358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout                final int count = ((ViewGroup) view).getChildCount();
1245358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout                for (int i = 0; view.hasTransientState() && i < count; i++) {
1255358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout                    cancelAnimationsRecursive(((ViewGroup) view).getChildAt(i));
1265358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout                }
1275358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout            }
1285358b0ca6ed795892bd097fdf15d41fb6b1a03d2Craig Stout        }
12947520b68e50572a9775a662410c5aff8300c8784Craig Stout    }
13047520b68e50572a9775a662410c5aff8300c8784Craig Stout
1317ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout    /**
1327ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     * Called to set a click listener for the given view holder.
1337ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     *
1347ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     * The default implementation sets the click listener on the root view in the view holder.
1357ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     * If the root view isn't focusable this method should be overridden to set the listener
1367ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     * on the appropriate focusable child view(s).
1377ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     *
1387ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     * @param holder The view holder containing the view(s) on which the listener should be set.
1397ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     * @param listener The click listener to be set.
1407ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout     */
1417ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout    public void setOnClickListener(ViewHolder holder, View.OnClickListener listener) {
1427ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout        holder.view.setOnClickListener(listener);
1437ab1edf2b49f3cdcb9db7a1c60d0dc1e17a9aef7Craig Stout    }
14420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn}
145