Home | History | Annotate | Download | only in widget
      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 androidx.leanback.widget;
     15 
     16 import android.util.Log;
     17 import android.view.View;
     18 import android.view.ViewGroup;
     19 
     20 import androidx.recyclerview.widget.RecyclerView;
     21 
     22 import java.util.ArrayList;
     23 import java.util.List;
     24 
     25 /**
     26  * Bridge from {@link Presenter} to {@link RecyclerView.Adapter}. Public to allow use by third
     27  * party Presenters.
     28  */
     29 public class ItemBridgeAdapter extends RecyclerView.Adapter implements FacetProviderAdapter {
     30     static final String TAG = "ItemBridgeAdapter";
     31     static final boolean DEBUG = false;
     32 
     33     /**
     34      * Interface for listening to ViewHolder operations.
     35      */
     36     public static class AdapterListener {
     37         public void onAddPresenter(Presenter presenter, int type) {
     38         }
     39 
     40         public void onCreate(ViewHolder viewHolder) {
     41         }
     42 
     43         public void onBind(ViewHolder viewHolder) {
     44         }
     45 
     46         public void onBind(ViewHolder viewHolder, List payloads) {
     47             onBind(viewHolder);
     48         }
     49 
     50         public void onUnbind(ViewHolder viewHolder) {
     51         }
     52 
     53         public void onAttachedToWindow(ViewHolder viewHolder) {
     54         }
     55 
     56         public void onDetachedFromWindow(ViewHolder viewHolder) {
     57         }
     58     }
     59 
     60     /**
     61      * Interface for wrapping a view created by a Presenter into another view.
     62      * The wrapper must be the immediate parent of the wrapped view.
     63      */
     64     public static abstract class Wrapper {
     65         public abstract View createWrapper(View root);
     66 
     67         public abstract void wrap(View wrapper, View wrapped);
     68     }
     69 
     70     private ObjectAdapter mAdapter;
     71     Wrapper mWrapper;
     72     private PresenterSelector mPresenterSelector;
     73     FocusHighlightHandler mFocusHighlight;
     74     private AdapterListener mAdapterListener;
     75     private ArrayList<Presenter> mPresenters = new ArrayList<Presenter>();
     76 
     77     final class OnFocusChangeListener implements View.OnFocusChangeListener {
     78         View.OnFocusChangeListener mChainedListener;
     79 
     80         @Override
     81         public void onFocusChange(View view, boolean hasFocus) {
     82             if (DEBUG) {
     83                 Log.v(TAG, "onFocusChange " + hasFocus + " " + view
     84                         + " mFocusHighlight" + mFocusHighlight);
     85             }
     86             if (mWrapper != null) {
     87                 view = (View) view.getParent();
     88             }
     89             if (mFocusHighlight != null) {
     90                 mFocusHighlight.onItemFocused(view, hasFocus);
     91             }
     92             if (mChainedListener != null) {
     93                 mChainedListener.onFocusChange(view, hasFocus);
     94             }
     95         }
     96     }
     97 
     98     /**
     99      * ViewHolder for the ItemBridgeAdapter.
    100      */
    101     public class ViewHolder extends RecyclerView.ViewHolder implements FacetProvider {
    102         final Presenter mPresenter;
    103         final Presenter.ViewHolder mHolder;
    104         final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener();
    105         Object mItem;
    106         Object mExtraObject;
    107 
    108         /**
    109          * Get {@link Presenter}.
    110          */
    111         public final Presenter getPresenter() {
    112             return mPresenter;
    113         }
    114 
    115         /**
    116          * Get {@link Presenter.ViewHolder}.
    117          */
    118         public final Presenter.ViewHolder getViewHolder() {
    119             return mHolder;
    120         }
    121 
    122         /**
    123          * Get currently bound object.
    124          */
    125         public final Object getItem() {
    126             return mItem;
    127         }
    128 
    129         /**
    130          * Get extra object associated with the view.  Developer can attach
    131          * any customized UI object in addition to {@link Presenter.ViewHolder}.
    132          * A typical use case is attaching an animator object.
    133          */
    134         public final Object getExtraObject() {
    135             return mExtraObject;
    136         }
    137 
    138         /**
    139          * Set extra object associated with the view.  Developer can attach
    140          * any customized UI object in addition to {@link Presenter.ViewHolder}.
    141          * A typical use case is attaching an animator object.
    142          */
    143         public void setExtraObject(Object object) {
    144             mExtraObject = object;
    145         }
    146 
    147         @Override
    148         public Object getFacet(Class<?> facetClass) {
    149             return mHolder.getFacet(facetClass);
    150         }
    151 
    152         ViewHolder(Presenter presenter, View view, Presenter.ViewHolder holder) {
    153             super(view);
    154             mPresenter = presenter;
    155             mHolder = holder;
    156         }
    157     }
    158 
    159     private ObjectAdapter.DataObserver mDataObserver = new ObjectAdapter.DataObserver() {
    160         @Override
    161         public void onChanged() {
    162             ItemBridgeAdapter.this.notifyDataSetChanged();
    163         }
    164 
    165         @Override
    166         public void onItemRangeChanged(int positionStart, int itemCount) {
    167             ItemBridgeAdapter.this.notifyItemRangeChanged(positionStart, itemCount);
    168         }
    169 
    170         @Override
    171         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
    172             ItemBridgeAdapter.this.notifyItemRangeChanged(positionStart, itemCount, payload);
    173         }
    174 
    175         @Override
    176         public void onItemRangeInserted(int positionStart, int itemCount) {
    177             ItemBridgeAdapter.this.notifyItemRangeInserted(positionStart, itemCount);
    178         }
    179 
    180         @Override
    181         public void onItemRangeRemoved(int positionStart, int itemCount) {
    182             ItemBridgeAdapter.this.notifyItemRangeRemoved(positionStart, itemCount);
    183         }
    184 
    185         @Override
    186         public void onItemMoved(int fromPosition, int toPosition) {
    187             ItemBridgeAdapter.this.notifyItemMoved(fromPosition, toPosition);
    188         }
    189     };
    190 
    191     public ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector) {
    192         setAdapter(adapter);
    193         mPresenterSelector = presenterSelector;
    194     }
    195 
    196     public ItemBridgeAdapter(ObjectAdapter adapter) {
    197         this(adapter, null);
    198     }
    199 
    200     public ItemBridgeAdapter() {
    201     }
    202 
    203     /**
    204      * Sets the {@link ObjectAdapter}.
    205      */
    206     public void setAdapter(ObjectAdapter adapter) {
    207         if (adapter == mAdapter) {
    208             return;
    209         }
    210         if (mAdapter != null) {
    211             mAdapter.unregisterObserver(mDataObserver);
    212         }
    213         mAdapter = adapter;
    214         if (mAdapter == null) {
    215             notifyDataSetChanged();
    216             return;
    217         }
    218 
    219         mAdapter.registerObserver(mDataObserver);
    220         if (hasStableIds() != mAdapter.hasStableIds()) {
    221             setHasStableIds(mAdapter.hasStableIds());
    222         }
    223         notifyDataSetChanged();
    224     }
    225 
    226     /**
    227      * Changes Presenter that creates and binds the view.
    228      *
    229      * @param presenterSelector Presenter that creates and binds the view.
    230      */
    231     public void setPresenter(PresenterSelector presenterSelector) {
    232         mPresenterSelector = presenterSelector;
    233         notifyDataSetChanged();
    234     }
    235 
    236     /**
    237      * Sets the {@link Wrapper}.
    238      */
    239     public void setWrapper(Wrapper wrapper) {
    240         mWrapper = wrapper;
    241     }
    242 
    243     /**
    244      * Returns the {@link Wrapper}.
    245      */
    246     public Wrapper getWrapper() {
    247         return mWrapper;
    248     }
    249 
    250     void setFocusHighlight(FocusHighlightHandler listener) {
    251         mFocusHighlight = listener;
    252         if (DEBUG) Log.v(TAG, "setFocusHighlight " + mFocusHighlight);
    253     }
    254 
    255     /**
    256      * Clears the adapter.
    257      */
    258     public void clear() {
    259         setAdapter(null);
    260     }
    261 
    262     /**
    263      * Sets the presenter mapper array.
    264      */
    265     public void setPresenterMapper(ArrayList<Presenter> presenters) {
    266         mPresenters = presenters;
    267     }
    268 
    269     /**
    270      * Returns the presenter mapper array.
    271      */
    272     public ArrayList<Presenter> getPresenterMapper() {
    273         return mPresenters;
    274     }
    275 
    276     @Override
    277     public int getItemCount() {
    278         return mAdapter != null ? mAdapter.size() : 0;
    279     }
    280 
    281     @Override
    282     public int getItemViewType(int position) {
    283         PresenterSelector presenterSelector = mPresenterSelector != null
    284                 ? mPresenterSelector : mAdapter.getPresenterSelector();
    285         Object item = mAdapter.get(position);
    286         Presenter presenter = presenterSelector.getPresenter(item);
    287         int type = mPresenters.indexOf(presenter);
    288         if (type < 0) {
    289             mPresenters.add(presenter);
    290             type = mPresenters.indexOf(presenter);
    291             if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
    292             onAddPresenter(presenter, type);
    293             if (mAdapterListener != null) {
    294                 mAdapterListener.onAddPresenter(presenter, type);
    295             }
    296         }
    297         return type;
    298     }
    299 
    300     /**
    301      * Called when presenter is added to Adapter.
    302      */
    303     protected void onAddPresenter(Presenter presenter, int type) {
    304     }
    305 
    306     /**
    307      * Called when ViewHolder is created.
    308      */
    309     protected void onCreate(ViewHolder viewHolder) {
    310     }
    311 
    312     /**
    313      * Called when ViewHolder has been bound to data.
    314      */
    315     protected void onBind(ViewHolder viewHolder) {
    316     }
    317 
    318     /**
    319      * Called when ViewHolder has been unbound from data.
    320      */
    321     protected void onUnbind(ViewHolder viewHolder) {
    322     }
    323 
    324     /**
    325      * Called when ViewHolder has been attached to window.
    326      */
    327     protected void onAttachedToWindow(ViewHolder viewHolder) {
    328     }
    329 
    330     /**
    331      * Called when ViewHolder has been detached from window.
    332      */
    333     protected void onDetachedFromWindow(ViewHolder viewHolder) {
    334     }
    335 
    336     /**
    337      * {@link View.OnFocusChangeListener} that assigned in
    338      * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
    339      * {@link View.OnFocusChangeListener} after that.
    340      */
    341     @Override
    342     public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    343         if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
    344         Presenter presenter = mPresenters.get(viewType);
    345         Presenter.ViewHolder presenterVh;
    346         View view;
    347         if (mWrapper != null) {
    348             view = mWrapper.createWrapper(parent);
    349             presenterVh = presenter.onCreateViewHolder(parent);
    350             mWrapper.wrap(view, presenterVh.view);
    351         } else {
    352             presenterVh = presenter.onCreateViewHolder(parent);
    353             view = presenterVh.view;
    354         }
    355         ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
    356         onCreate(viewHolder);
    357         if (mAdapterListener != null) {
    358             mAdapterListener.onCreate(viewHolder);
    359         }
    360         View presenterView = viewHolder.mHolder.view;
    361         if (presenterView != null) {
    362             viewHolder.mFocusChangeListener.mChainedListener =
    363                     presenterView.getOnFocusChangeListener();
    364             presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
    365         }
    366         if (mFocusHighlight != null) {
    367             mFocusHighlight.onInitializeView(view);
    368         }
    369         return viewHolder;
    370     }
    371 
    372     /**
    373      * Sets the AdapterListener.
    374      */
    375     public void setAdapterListener(AdapterListener listener) {
    376         mAdapterListener = listener;
    377     }
    378 
    379     @Override
    380     public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    381         if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
    382         ViewHolder viewHolder = (ViewHolder) holder;
    383         viewHolder.mItem = mAdapter.get(position);
    384 
    385         viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
    386 
    387         onBind(viewHolder);
    388         if (mAdapterListener != null) {
    389             mAdapterListener.onBind(viewHolder);
    390         }
    391     }
    392 
    393     @Override
    394     public final  void onBindViewHolder(RecyclerView.ViewHolder holder, int position,
    395             List payloads) {
    396         if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
    397         ViewHolder viewHolder = (ViewHolder) holder;
    398         viewHolder.mItem = mAdapter.get(position);
    399 
    400         viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem, payloads);
    401 
    402         onBind(viewHolder);
    403         if (mAdapterListener != null) {
    404             mAdapterListener.onBind(viewHolder, payloads);
    405         }
    406     }
    407 
    408     @Override
    409     public final void onViewRecycled(RecyclerView.ViewHolder holder) {
    410         ViewHolder viewHolder = (ViewHolder) holder;
    411         viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder);
    412         onUnbind(viewHolder);
    413         if (mAdapterListener != null) {
    414             mAdapterListener.onUnbind(viewHolder);
    415         }
    416         viewHolder.mItem = null;
    417     }
    418 
    419     @Override
    420     public final boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {
    421         onViewRecycled(holder);
    422         return false;
    423     }
    424 
    425     @Override
    426     public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
    427         ViewHolder viewHolder = (ViewHolder) holder;
    428         onAttachedToWindow(viewHolder);
    429         if (mAdapterListener != null) {
    430             mAdapterListener.onAttachedToWindow(viewHolder);
    431         }
    432         viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder);
    433     }
    434 
    435     @Override
    436     public final void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
    437         ViewHolder viewHolder = (ViewHolder) holder;
    438         viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder);
    439         onDetachedFromWindow(viewHolder);
    440         if (mAdapterListener != null) {
    441             mAdapterListener.onDetachedFromWindow(viewHolder);
    442         }
    443     }
    444 
    445     @Override
    446     public long getItemId(int position) {
    447         return mAdapter.getId(position);
    448     }
    449 
    450     @Override
    451     public FacetProvider getFacetProvider(int type) {
    452         return mPresenters.get(type);
    453     }
    454 }
    455