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