VerticalGridPresenter.java revision afe1da48b4673f64c67c130237dd485a34a69c1b
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.v17.leanback.R; 17import android.view.LayoutInflater; 18import android.view.View; 19import android.view.ViewGroup; 20import android.view.ViewGroup.LayoutParams; 21import android.util.Log; 22 23/** 24 * A presenter that renders objects in a vertical grid. 25 * 26 */ 27public class VerticalGridPresenter extends Presenter { 28 private static final String TAG = "GridPresenter"; 29 private static final boolean DEBUG = false; 30 31 public static class ViewHolder extends Presenter.ViewHolder { 32 final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter(); 33 final VerticalGridView mGridView; 34 boolean mInitialized; 35 36 public ViewHolder(VerticalGridView view) { 37 super(view); 38 mGridView = view; 39 } 40 41 public VerticalGridView getGridView() { 42 return mGridView; 43 } 44 } 45 46 private int mNumColumns = -1; 47 private int mZoomFactor; 48 private boolean mShadowEnabled = true; 49 private OnItemClickedListener mOnItemClickedListener; 50 private OnItemSelectedListener mOnItemSelectedListener; 51 private OnItemViewSelectedListener mOnItemViewSelectedListener; 52 private OnItemViewClickedListener mOnItemViewClickedListener; 53 54 public VerticalGridPresenter() { 55 this(FocusHighlight.ZOOM_FACTOR_MEDIUM); 56 } 57 58 public VerticalGridPresenter(int zoomFactor) { 59 mZoomFactor = zoomFactor; 60 } 61 62 /** 63 * Sets the number of columns in the vertical grid. 64 */ 65 public void setNumberOfColumns(int numColumns) { 66 if (numColumns < 0) { 67 throw new IllegalArgumentException("Invalid number of columns"); 68 } 69 if (mNumColumns != numColumns) { 70 mNumColumns = numColumns; 71 } 72 } 73 74 /** 75 * Returns the number of columns in the vertical grid. 76 */ 77 public int getNumberOfColumns() { 78 return mNumColumns; 79 } 80 81 /** 82 * Enable or disable child shadow. 83 * This is not only for enable/disable default shadow implementation but also subclass must 84 * respect this flag. 85 */ 86 public final void setShadowEnabled(boolean enabled) { 87 mShadowEnabled = enabled; 88 } 89 90 /** 91 * Returns true if child shadow is enabled. 92 * This is not only for enable/disable default shadow implementation but also subclass must 93 * respect this flag. 94 */ 95 public final boolean getShadowEnabled() { 96 return mShadowEnabled; 97 } 98 99 /** 100 * Returns true if opticalBounds is supported (SDK >= 18) so that default shadow 101 * is applied to each individual child of {@link VerticalGridView}. 102 * Subclass may return false to disable. 103 */ 104 public boolean isUsingDefaultShadow() { 105 return ShadowOverlayContainer.supportsShadow(); 106 } 107 108 /** 109 * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled 110 * on each child of vertical grid. If subclass returns false in isUsingDefaultShadow() 111 * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false. 112 */ 113 public boolean isUsingZOrder() { 114 return ShadowHelper.getInstance().usesZShadow(); 115 } 116 117 final boolean needsDefaultShadow() { 118 return isUsingDefaultShadow() && getShadowEnabled(); 119 } 120 121 @Override 122 public final ViewHolder onCreateViewHolder(ViewGroup parent) { 123 ViewHolder vh = createGridViewHolder(parent); 124 vh.mInitialized = false; 125 initializeGridViewHolder(vh); 126 if (!vh.mInitialized) { 127 throw new RuntimeException("super.initializeGridViewHolder() must be called"); 128 } 129 return vh; 130 } 131 132 /** 133 * Subclass may override this to inflate a different layout. 134 */ 135 protected ViewHolder createGridViewHolder(ViewGroup parent) { 136 View root = LayoutInflater.from(parent.getContext()).inflate( 137 R.layout.lb_vertical_grid, parent, false); 138 return new ViewHolder((VerticalGridView) root.findViewById(R.id.browse_grid)); 139 } 140 141 private ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { 142 @Override 143 public View createWrapper(View root) { 144 ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext()); 145 wrapper.setLayoutParams( 146 new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 147 wrapper.initialize(needsDefaultShadow(), false); 148 return wrapper; 149 } 150 @Override 151 public void wrap(View wrapper, View wrapped) { 152 ((ShadowOverlayContainer) wrapper).wrap(wrapped); 153 } 154 }; 155 156 /** 157 * Called after a {@link VerticalGridPresenter.ViewHolder} is created. 158 * Subclasses may override this method and start by calling 159 * super.initializeGridViewHolder(ViewHolder). 160 * 161 * @param vh The ViewHolder to initialize for the vertical grid. 162 */ 163 protected void initializeGridViewHolder(ViewHolder vh) { 164 if (mNumColumns == -1) { 165 throw new IllegalStateException("Number of columns must be set"); 166 } 167 if (DEBUG) Log.v(TAG, "mNumColumns " + mNumColumns); 168 vh.getGridView().setNumColumns(mNumColumns); 169 vh.mInitialized = true; 170 171 if (needsDefaultShadow()) { 172 vh.mItemBridgeAdapter.setWrapper(mWrapper); 173 ShadowOverlayContainer.prepareParentForShadow(vh.getGridView()); 174 ((ViewGroup) vh.view).setClipChildren(false); 175 } 176 vh.getGridView().setFocusDrawingOrderEnabled(!isUsingZOrder()); 177 FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter, mZoomFactor); 178 179 final ViewHolder gridViewHolder = vh; 180 vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() { 181 @Override 182 public void onChildSelected(ViewGroup parent, View view, int position, long id) { 183 selectChildView(gridViewHolder, view); 184 } 185 }); 186 187 vh.mItemBridgeAdapter.setAdapterListener(new ItemBridgeAdapter.AdapterListener() { 188 @Override 189 public void onCreate(final ItemBridgeAdapter.ViewHolder itemViewHolder) { 190 // Only when having an OnItemClickListner, we attach the OnClickListener. 191 if (getOnItemClickedListener() != null || getOnItemViewClickedListener() != null) { 192 final View itemView = itemViewHolder.getViewHolder().view; 193 itemView.setOnClickListener(new View.OnClickListener() { 194 @Override 195 public void onClick(View view) { 196 if (getOnItemClickedListener() != null) { 197 // Row is always null 198 getOnItemClickedListener().onItemClicked(itemViewHolder.mItem, 199 null); 200 } 201 if (getOnItemViewClickedListener() != null) { 202 // Row is always null 203 getOnItemViewClickedListener().onItemClicked( 204 itemViewHolder.mHolder, itemViewHolder.mItem, null, null); 205 } 206 } 207 }); 208 } 209 } 210 211 @Override 212 public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) { 213 viewHolder.itemView.setActivated(true); 214 } 215 }); 216 } 217 218 @Override 219 public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { 220 if (DEBUG) Log.v(TAG, "onBindViewHolder " + item); 221 ViewHolder vh = (ViewHolder) viewHolder; 222 vh.mItemBridgeAdapter.setAdapter((ObjectAdapter) item); 223 vh.getGridView().setAdapter(vh.mItemBridgeAdapter); 224 } 225 226 @Override 227 public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { 228 if (DEBUG) Log.v(TAG, "onUnbindViewHolder"); 229 ViewHolder vh = (ViewHolder) viewHolder; 230 vh.mItemBridgeAdapter.setAdapter(null); 231 vh.getGridView().setAdapter(null); 232 } 233 234 /** 235 * Sets the item selected listener. 236 * Since this is a grid the row parameter is always null. 237 * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)} 238 */ 239 public final void setOnItemSelectedListener(OnItemSelectedListener listener) { 240 mOnItemSelectedListener = listener; 241 } 242 243 /** 244 * Returns the item selected listener. 245 * @deprecated Use {@link #getOnItemViewSelectedListener()} 246 */ 247 public final OnItemSelectedListener getOnItemSelectedListener() { 248 return mOnItemSelectedListener; 249 } 250 251 /** 252 * Sets the item selected listener. 253 * Since this is a grid the row parameter is always null. 254 */ 255 public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) { 256 mOnItemViewSelectedListener = listener; 257 } 258 259 /** 260 * Returns the item selected listener. 261 */ 262 public final OnItemViewSelectedListener getOnItemViewSelectedListener() { 263 return mOnItemViewSelectedListener; 264 } 265 266 /** 267 * Sets the item clicked listener. 268 * OnItemClickedListener will override {@link View.OnClickListener} that 269 * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}. 270 * So in general, developer should choose one of the listeners but not both. 271 * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)} 272 */ 273 public final void setOnItemClickedListener(OnItemClickedListener listener) { 274 mOnItemClickedListener = listener; 275 } 276 277 /** 278 * Sets the item clicked listener. 279 * OnItemViewClickedListener will override {@link View.OnClickListener} that 280 * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}. 281 * So in general, developer should choose one of the listeners but not both. 282 */ 283 public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) { 284 mOnItemViewClickedListener = listener; 285 } 286 287 /** 288 * Returns the item clicked listener. 289 * @deprecated Use {@link #getOnItemViewClickedListener()} 290 */ 291 public final OnItemClickedListener getOnItemClickedListener() { 292 return mOnItemClickedListener; 293 } 294 295 /** 296 * Returns the item clicked listener. 297 */ 298 public final OnItemViewClickedListener getOnItemViewClickedListener() { 299 return mOnItemViewClickedListener; 300 } 301 302 private void selectChildView(ViewHolder vh, View view) { 303 if (getOnItemSelectedListener() != null) { 304 ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null : 305 (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view); 306 if (ibh == null) { 307 getOnItemSelectedListener().onItemSelected(null, null); 308 } else { 309 getOnItemSelectedListener().onItemSelected(ibh.mItem, null); 310 } 311 } 312 if (getOnItemViewSelectedListener() != null) { 313 ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null : 314 (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view); 315 if (ibh == null) { 316 getOnItemViewSelectedListener().onItemSelected(null, null, null, null); 317 } else { 318 getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem, null, null); 319 } 320 } 321 } 322} 323