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