ItemBridgeAdapter.java revision 739e3805bf2785e6773aede5e2e1643f537305f9
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) { 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 @Override 202 public int getItemCount() { 203 return mAdapter.size(); 204 } 205 206 @Override 207 public int getItemViewType(int position) { 208 PresenterSelector presenterSelector = mPresenterSelector != null ? 209 mPresenterSelector : mAdapter.getPresenterSelector(); 210 Object item = mAdapter.get(position); 211 Presenter presenter = presenterSelector.getPresenter(item); 212 int type = mPresenters.indexOf(presenter); 213 if (type < 0) { 214 mPresenters.add(presenter); 215 type = mPresenters.indexOf(presenter); 216 if (mAdapterListener != null) { 217 mAdapterListener.onAddPresenter(presenter); 218 } 219 } 220 return type; 221 } 222 223 /** 224 * {@link View.OnFocusChangeListener} that assigned in 225 * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change 226 * {@link View.OnFocusChangeListener} after that. 227 */ 228 @Override 229 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 230 if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType); 231 Presenter presenter = mPresenters.get(viewType); 232 Presenter.ViewHolder presenterVh; 233 View view; 234 if (mWrapper != null) { 235 view = mWrapper.createWrapper(parent); 236 presenterVh = presenter.onCreateViewHolder(parent); 237 mWrapper.wrap(view, presenterVh.view); 238 } else { 239 presenterVh = presenter.onCreateViewHolder(parent); 240 view = presenterVh.view; 241 } 242 ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh); 243 if (mAdapterListener != null) { 244 mAdapterListener.onCreate(viewHolder); 245 } 246 View presenterView = viewHolder.mHolder.view; 247 if (presenterView != null) { 248 viewHolder.mFocusChangeListener.mChainedListener = presenterView.getOnFocusChangeListener(); 249 presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener); 250 } 251 return viewHolder; 252 } 253 254 public void setAdapterListener(AdapterListener listener) { 255 mAdapterListener = listener; 256 } 257 258 @Override 259 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 260 if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position); 261 ViewHolder viewHolder = (ViewHolder) holder; 262 viewHolder.mItem = mAdapter.get(position); 263 264 viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem); 265 266 if (mAdapterListener != null) { 267 mAdapterListener.onBind(viewHolder); 268 } 269 } 270 271 @Override 272 public void onViewRecycled(RecyclerView.ViewHolder holder) { 273 ViewHolder viewHolder = (ViewHolder) holder; 274 viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder); 275 276 viewHolder.mItem = null; 277 278 if (mAdapterListener != null) { 279 mAdapterListener.onUnbind(viewHolder); 280 } 281 } 282 283 @Override 284 public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { 285 ViewHolder viewHolder = (ViewHolder) holder; 286 if (mAdapterListener != null) { 287 mAdapterListener.onAttachedToWindow(viewHolder); 288 } 289 viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder); 290 } 291 292 @Override 293 public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) { 294 ViewHolder viewHolder = (ViewHolder) holder; 295 viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder); 296 if (mAdapterListener != null) { 297 mAdapterListener.onDetachedFromWindow(viewHolder); 298 } 299 } 300 301 @Override 302 public long getItemId(int position) { 303 return mAdapter.getId(position); 304 } 305 306} 307