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