ItemBridgeAdapter.java revision 86a6309c3e89ec6abc40ec045bfaef7827cbe427
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 (mAdapter != null) { 181 mAdapter.unregisterObserver(mDataObserver); 182 } 183 mAdapter = adapter; 184 if (mAdapter == null) { 185 return; 186 } 187 188 mAdapter.registerObserver(mDataObserver); 189 if (hasStableIds() != mAdapter.hasStableIds()) { 190 setHasStableIds(mAdapter.hasStableIds()); 191 } 192 } 193 194 /** 195 * Sets the {@link Wrapper}. 196 */ 197 public void setWrapper(Wrapper wrapper) { 198 mWrapper = wrapper; 199 } 200 201 /** 202 * Returns the {@link Wrapper}. 203 */ 204 public Wrapper getWrapper() { 205 return mWrapper; 206 } 207 208 void setFocusHighlight(FocusHighlightHandler listener) { 209 mFocusHighlight = listener; 210 if (DEBUG) Log.v(TAG, "setFocusHighlight " + mFocusHighlight); 211 } 212 213 /** 214 * Clears the adapter. 215 */ 216 public void clear() { 217 setAdapter(null); 218 } 219 220 /** 221 * Sets the presenter mapper array. 222 */ 223 public void setPresenterMapper(ArrayList<Presenter> presenters) { 224 mPresenters = presenters; 225 } 226 227 /** 228 * Returns the presenter mapper array. 229 */ 230 public ArrayList<Presenter> getPresenterMapper() { 231 return mPresenters; 232 } 233 234 @Override 235 public int getItemCount() { 236 return mAdapter.size(); 237 } 238 239 @Override 240 public int getItemViewType(int position) { 241 PresenterSelector presenterSelector = mPresenterSelector != null ? 242 mPresenterSelector : mAdapter.getPresenterSelector(); 243 Object item = mAdapter.get(position); 244 Presenter presenter = presenterSelector.getPresenter(item); 245 int type = mPresenters.indexOf(presenter); 246 if (type < 0) { 247 mPresenters.add(presenter); 248 type = mPresenters.indexOf(presenter); 249 if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type); 250 onAddPresenter(presenter, type); 251 if (mAdapterListener != null) { 252 mAdapterListener.onAddPresenter(presenter, type); 253 } 254 } 255 return type; 256 } 257 258 /** 259 * Called when presenter is added to Adapter. 260 */ 261 protected void onAddPresenter(Presenter presenter, int type) { 262 } 263 264 /** 265 * Called when ViewHolder is created. 266 */ 267 protected void onCreate(ViewHolder viewHolder) { 268 } 269 270 /** 271 * Called when ViewHolder has been bound to data. 272 */ 273 protected void onBind(ViewHolder viewHolder) { 274 } 275 276 /** 277 * Called when ViewHolder has been unbound from data. 278 */ 279 protected void onUnbind(ViewHolder viewHolder) { 280 } 281 282 /** 283 * Called when ViewHolder has been attached to window. 284 */ 285 protected void onAttachedToWindow(ViewHolder viewHolder) { 286 } 287 288 /** 289 * Called when ViewHolder has been detached from window. 290 */ 291 protected void onDetachedFromWindow(ViewHolder viewHolder) { 292 } 293 294 /** 295 * {@link View.OnFocusChangeListener} that assigned in 296 * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change 297 * {@link View.OnFocusChangeListener} after that. 298 */ 299 @Override 300 public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 301 if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType); 302 Presenter presenter = mPresenters.get(viewType); 303 Presenter.ViewHolder presenterVh; 304 View view; 305 if (mWrapper != null) { 306 view = mWrapper.createWrapper(parent); 307 presenterVh = presenter.onCreateViewHolder(parent); 308 mWrapper.wrap(view, presenterVh.view); 309 } else { 310 presenterVh = presenter.onCreateViewHolder(parent); 311 view = presenterVh.view; 312 } 313 ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh); 314 onCreate(viewHolder); 315 if (mAdapterListener != null) { 316 mAdapterListener.onCreate(viewHolder); 317 } 318 View presenterView = viewHolder.mHolder.view; 319 if (presenterView != null) { 320 viewHolder.mFocusChangeListener.mChainedListener = presenterView.getOnFocusChangeListener(); 321 presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener); 322 } 323 if (mFocusHighlight != null) { 324 mFocusHighlight.onInitializeView(view); 325 } 326 return viewHolder; 327 } 328 329 /** 330 * Sets the AdapterListener. 331 */ 332 public void setAdapterListener(AdapterListener listener) { 333 mAdapterListener = listener; 334 } 335 336 @Override 337 public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 338 if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position); 339 ViewHolder viewHolder = (ViewHolder) holder; 340 viewHolder.mItem = mAdapter.get(position); 341 342 viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem); 343 344 onBind(viewHolder); 345 if (mAdapterListener != null) { 346 mAdapterListener.onBind(viewHolder); 347 } 348 } 349 350 @Override 351 public final void onViewRecycled(RecyclerView.ViewHolder holder) { 352 ViewHolder viewHolder = (ViewHolder) holder; 353 viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder); 354 onUnbind(viewHolder); 355 if (mAdapterListener != null) { 356 mAdapterListener.onUnbind(viewHolder); 357 } 358 viewHolder.mItem = null; 359 } 360 361 @Override 362 public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { 363 ViewHolder viewHolder = (ViewHolder) holder; 364 onAttachedToWindow(viewHolder); 365 if (mAdapterListener != null) { 366 mAdapterListener.onAttachedToWindow(viewHolder); 367 } 368 viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder); 369 } 370 371 @Override 372 public final void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) { 373 ViewHolder viewHolder = (ViewHolder) holder; 374 viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder); 375 onDetachedFromWindow(viewHolder); 376 if (mAdapterListener != null) { 377 mAdapterListener.onDetachedFromWindow(viewHolder); 378 } 379 } 380 381 @Override 382 public long getItemId(int position) { 383 return mAdapter.getId(position); 384 } 385 386 @Override 387 public FacetProvider getFacetProvider(int type) { 388 return mPresenters.get(type); 389 } 390} 391