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