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 */ 14package android.support.v17.leanback.widget; 15 16import android.support.v7.widget.RecyclerView; 17import android.util.Log; 18import android.view.View; 19import android.view.ViewGroup; 20 21import java.util.ArrayList; 22 23/** 24 * Bridge from {@link Presenter} to {@link RecyclerView.Adapter}. Public to allow use by third 25 * party Presenters. 26 */ 27public class ItemBridgeAdapter extends RecyclerView.Adapter implements FacetProviderAdapter { 28 private static final String TAG = "ItemBridgeAdapter"; 29 private static final boolean DEBUG = false; 30 31 /** 32 * Interface for listening to ViewHolder operations. 33 */ 34 public static class AdapterListener { 35 public void onAddPresenter(Presenter presenter, int type) { 36 } 37 public void onCreate(ViewHolder viewHolder) { 38 } 39 public void onBind(ViewHolder viewHolder) { 40 } 41 public void onUnbind(ViewHolder viewHolder) { 42 } 43 public void onAttachedToWindow(ViewHolder viewHolder) { 44 } 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 { 54 public abstract View createWrapper(View root); 55 public abstract void wrap(View wrapper, View wrapped); 56 } 57 58 private ObjectAdapter mAdapter; 59 private Wrapper mWrapper; 60 private PresenterSelector mPresenterSelector; 61 private 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 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 */ 97 public final Presenter getPresenter() { 98 return mPresenter; 99 } 100 101 /** 102 * Get {@link Presenter.ViewHolder}. 103 */ 104 public final Presenter.ViewHolder getViewHolder() { 105 return mHolder; 106 } 107 108 /** 109 * Get currently bound object. 110 */ 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 */ 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 */ 129 public void setExtraObject(Object object) { 130 mExtraObject = object; 131 } 132 133 @Override 134 public Object getFacet(Class<?> facetClass) { 135 return mHolder.getFacet(facetClass); 136 } 137 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 164 public ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector) { 165 setAdapter(adapter); 166 mPresenterSelector = presenterSelector; 167 } 168 169 public ItemBridgeAdapter(ObjectAdapter adapter) { 170 this(adapter, null); 171 } 172 173 public ItemBridgeAdapter() { 174 } 175 176 /** 177 * Sets the {@link ObjectAdapter}. 178 */ 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 * Sets the {@link Wrapper}. 201 */ 202 public void setWrapper(Wrapper wrapper) { 203 mWrapper = wrapper; 204 } 205 206 /** 207 * Returns the {@link Wrapper}. 208 */ 209 public Wrapper getWrapper() { 210 return mWrapper; 211 } 212 213 void setFocusHighlight(FocusHighlightHandler listener) { 214 mFocusHighlight = listener; 215 if (DEBUG) Log.v(TAG, "setFocusHighlight " + mFocusHighlight); 216 } 217 218 /** 219 * Clears the adapter. 220 */ 221 public void clear() { 222 setAdapter(null); 223 } 224 225 /** 226 * Sets the presenter mapper array. 227 */ 228 public void setPresenterMapper(ArrayList<Presenter> presenters) { 229 mPresenters = presenters; 230 } 231 232 /** 233 * Returns the presenter mapper array. 234 */ 235 public ArrayList<Presenter> getPresenterMapper() { 236 return mPresenters; 237 } 238 239 @Override 240 public int getItemCount() { 241 return mAdapter != null ? mAdapter.size() : 0; 242 } 243 244 @Override 245 public int getItemViewType(int position) { 246 PresenterSelector presenterSelector = mPresenterSelector != null ? 247 mPresenterSelector : mAdapter.getPresenterSelector(); 248 Object item = mAdapter.get(position); 249 Presenter presenter = presenterSelector.getPresenter(item); 250 int type = mPresenters.indexOf(presenter); 251 if (type < 0) { 252 mPresenters.add(presenter); 253 type = mPresenters.indexOf(presenter); 254 if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type); 255 onAddPresenter(presenter, type); 256 if (mAdapterListener != null) { 257 mAdapterListener.onAddPresenter(presenter, type); 258 } 259 } 260 return type; 261 } 262 263 /** 264 * Called when presenter is added to Adapter. 265 */ 266 protected void onAddPresenter(Presenter presenter, int type) { 267 } 268 269 /** 270 * Called when ViewHolder is created. 271 */ 272 protected void onCreate(ViewHolder viewHolder) { 273 } 274 275 /** 276 * Called when ViewHolder has been bound to data. 277 */ 278 protected void onBind(ViewHolder viewHolder) { 279 } 280 281 /** 282 * Called when ViewHolder has been unbound from data. 283 */ 284 protected void onUnbind(ViewHolder viewHolder) { 285 } 286 287 /** 288 * Called when ViewHolder has been attached to window. 289 */ 290 protected void onAttachedToWindow(ViewHolder viewHolder) { 291 } 292 293 /** 294 * Called when ViewHolder has been detached from window. 295 */ 296 protected void onDetachedFromWindow(ViewHolder viewHolder) { 297 } 298 299 /** 300 * {@link View.OnFocusChangeListener} that assigned in 301 * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change 302 * {@link View.OnFocusChangeListener} after that. 303 */ 304 @Override 305 public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 306 if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType); 307 Presenter presenter = mPresenters.get(viewType); 308 Presenter.ViewHolder presenterVh; 309 View view; 310 if (mWrapper != null) { 311 view = mWrapper.createWrapper(parent); 312 presenterVh = presenter.onCreateViewHolder(parent); 313 mWrapper.wrap(view, presenterVh.view); 314 } else { 315 presenterVh = presenter.onCreateViewHolder(parent); 316 view = presenterVh.view; 317 } 318 ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh); 319 onCreate(viewHolder); 320 if (mAdapterListener != null) { 321 mAdapterListener.onCreate(viewHolder); 322 } 323 View presenterView = viewHolder.mHolder.view; 324 if (presenterView != null) { 325 viewHolder.mFocusChangeListener.mChainedListener = presenterView.getOnFocusChangeListener(); 326 presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener); 327 } 328 if (mFocusHighlight != null) { 329 mFocusHighlight.onInitializeView(view); 330 } 331 return viewHolder; 332 } 333 334 /** 335 * Sets the AdapterListener. 336 */ 337 public void setAdapterListener(AdapterListener listener) { 338 mAdapterListener = listener; 339 } 340 341 @Override 342 public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 343 if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position); 344 ViewHolder viewHolder = (ViewHolder) holder; 345 viewHolder.mItem = mAdapter.get(position); 346 347 viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem); 348 349 onBind(viewHolder); 350 if (mAdapterListener != null) { 351 mAdapterListener.onBind(viewHolder); 352 } 353 } 354 355 @Override 356 public final void onViewRecycled(RecyclerView.ViewHolder holder) { 357 ViewHolder viewHolder = (ViewHolder) holder; 358 viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder); 359 onUnbind(viewHolder); 360 if (mAdapterListener != null) { 361 mAdapterListener.onUnbind(viewHolder); 362 } 363 viewHolder.mItem = null; 364 } 365 366 @Override 367 public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { 368 ViewHolder viewHolder = (ViewHolder) holder; 369 onAttachedToWindow(viewHolder); 370 if (mAdapterListener != null) { 371 mAdapterListener.onAttachedToWindow(viewHolder); 372 } 373 viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder); 374 } 375 376 @Override 377 public final void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) { 378 ViewHolder viewHolder = (ViewHolder) holder; 379 viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder); 380 onDetachedFromWindow(viewHolder); 381 if (mAdapterListener != null) { 382 mAdapterListener.onDetachedFromWindow(viewHolder); 383 } 384 } 385 386 @Override 387 public long getItemId(int position) { 388 return mAdapter.getId(position); 389 } 390 391 @Override 392 public FacetProvider getFacetProvider(int type) { 393 return mPresenters.get(type); 394 } 395} 396