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