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