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