VerticalGridFragment.java revision 5d00775a5d3ce3c1fc06c4de0945d1b8f03aadc4
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.app; 15 16import android.support.v17.leanback.R; 17import android.support.v17.leanback.transition.TransitionHelper; 18import android.support.v17.leanback.widget.BrowseFrameLayout; 19import android.support.v17.leanback.widget.OnItemViewClickedListener; 20import android.support.v17.leanback.widget.OnItemViewSelectedListener; 21import android.support.v17.leanback.widget.Presenter; 22import android.support.v17.leanback.widget.Row; 23import android.support.v17.leanback.widget.RowPresenter; 24import android.support.v17.leanback.widget.TitleView; 25import android.support.v17.leanback.widget.VerticalGridPresenter; 26import android.support.v17.leanback.widget.ObjectAdapter; 27import android.support.v17.leanback.widget.OnItemClickedListener; 28import android.support.v17.leanback.widget.OnItemSelectedListener; 29import android.support.v17.leanback.widget.SearchOrbView; 30import android.app.Fragment; 31import android.graphics.drawable.Drawable; 32import android.os.Bundle; 33import android.util.Log; 34import android.view.LayoutInflater; 35import android.view.View; 36import android.view.ViewGroup; 37import android.view.ViewGroup.MarginLayoutParams; 38import android.widget.ImageView; 39import android.widget.TextView; 40 41/** 42 * A fragment for creating leanback vertical grids. 43 * 44 * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and 45 * an {@link ObjectAdapter}. 46 */ 47public class VerticalGridFragment extends Fragment { 48 private static final String TAG = "VerticalGridFragment"; 49 private static boolean DEBUG = false; 50 51 private BrowseFrameLayout mBrowseFrame; 52 private String mTitle; 53 private Drawable mBadgeDrawable; 54 private ObjectAdapter mAdapter; 55 private VerticalGridPresenter mGridPresenter; 56 private VerticalGridPresenter.ViewHolder mGridViewHolder; 57 private OnItemSelectedListener mOnItemSelectedListener; 58 private OnItemClickedListener mOnItemClickedListener; 59 private OnItemViewSelectedListener mOnItemViewSelectedListener; 60 private OnItemViewClickedListener mOnItemViewClickedListener; 61 private View.OnClickListener mExternalOnSearchClickedListener; 62 private int mSelectedPosition = -1; 63 64 private TitleView mTitleView; 65 private SearchOrbView.Colors mSearchAffordanceColors; 66 private boolean mSearchAffordanceColorSet; 67 private boolean mShowingTitle = true; 68 69 // transition related 70 private static TransitionHelper sTransitionHelper = TransitionHelper.getInstance(); 71 private Object mTitleUpTransition; 72 private Object mTitleDownTransition; 73 private Object mSceneWithTitle; 74 private Object mSceneWithoutTitle; 75 76 /** 77 * Sets the badge drawable displayed in the title area. 78 */ 79 public void setBadgeDrawable(Drawable drawable) { 80 if (drawable != mBadgeDrawable) { 81 mBadgeDrawable = drawable; 82 if (mTitleView != null) { 83 mTitleView.setBadgeDrawable(drawable); 84 } 85 } 86 } 87 88 /** 89 * Returns the badge drawable. 90 */ 91 public Drawable getBadgeDrawable() { 92 return mBadgeDrawable; 93 } 94 95 /** 96 * Sets a title for the fragment. 97 */ 98 public void setTitle(String title) { 99 mTitle = title; 100 if (mTitleView != null) { 101 mTitleView.setTitle(mTitle); 102 } 103 } 104 105 /** 106 * Returns the title for the fragment. 107 */ 108 public String getTitle() { 109 return mTitle; 110 } 111 112 /** 113 * Sets the grid presenter. 114 */ 115 public void setGridPresenter(VerticalGridPresenter gridPresenter) { 116 if (gridPresenter == null) { 117 throw new IllegalArgumentException("Grid presenter may not be null"); 118 } 119 mGridPresenter = gridPresenter; 120 mGridPresenter.setOnItemViewSelectedListener(mRowSelectedListener); 121 if (mOnItemViewClickedListener != null) { 122 mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener); 123 } 124 if (mOnItemClickedListener != null) { 125 mGridPresenter.setOnItemClickedListener(mOnItemClickedListener); 126 } 127 } 128 129 /** 130 * Returns the grid presenter. 131 */ 132 public VerticalGridPresenter getGridPresenter() { 133 return mGridPresenter; 134 } 135 136 /** 137 * Sets the object adapter for the fragment. 138 */ 139 public void setAdapter(ObjectAdapter adapter) { 140 mAdapter = adapter; 141 updateAdapter(); 142 } 143 144 /** 145 * Returns the object adapter. 146 */ 147 public ObjectAdapter getAdapter() { 148 return mAdapter; 149 } 150 151 final private OnItemViewSelectedListener mRowSelectedListener = 152 new OnItemViewSelectedListener() { 153 @Override 154 public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, 155 RowPresenter.ViewHolder rowViewHolder, Row row) { 156 int position = mGridViewHolder.getGridView().getSelectedPosition(); 157 if (DEBUG) Log.v(TAG, "row selected position " + position); 158 onRowSelected(position); 159 if (mOnItemSelectedListener != null) { 160 mOnItemSelectedListener.onItemSelected(item, row); 161 } 162 if (mOnItemViewSelectedListener != null) { 163 mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item, 164 rowViewHolder, row); 165 } 166 } 167 }; 168 169 /** 170 * Sets an item selection listener. 171 * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)} 172 */ 173 public void setOnItemSelectedListener(OnItemSelectedListener listener) { 174 mOnItemSelectedListener = listener; 175 } 176 177 /** 178 * Sets an item selection listener. 179 */ 180 public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) { 181 mOnItemViewSelectedListener = listener; 182 } 183 184 private void onRowSelected(int position) { 185 if (position != mSelectedPosition) { 186 if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(position)) { 187 // if has no sibling in front of it, show title 188 if (!mShowingTitle) { 189 sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition); 190 mShowingTitle = true; 191 } 192 } else if (mShowingTitle) { 193 sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition); 194 mShowingTitle = false; 195 } 196 mSelectedPosition = position; 197 } 198 } 199 200 /** 201 * Sets an item clicked listener. 202 * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)} 203 */ 204 public void setOnItemClickedListener(OnItemClickedListener listener) { 205 mOnItemClickedListener = listener; 206 if (mGridPresenter != null) { 207 mGridPresenter.setOnItemClickedListener(mOnItemClickedListener); 208 } 209 } 210 211 /** 212 * Returns the item clicked listener. 213 * @deprecated Use {@link #getOnItemViewClickedListener()} 214 */ 215 public OnItemClickedListener getOnItemClickedListener() { 216 return mOnItemClickedListener; 217 } 218 219 /** 220 * Sets an item clicked listener. 221 */ 222 public void setOnItemViewClickedListener(OnItemViewClickedListener listener) { 223 mOnItemViewClickedListener = listener; 224 if (mGridPresenter != null) { 225 mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener); 226 } 227 } 228 229 /** 230 * Returns the item clicked listener. 231 */ 232 public OnItemViewClickedListener getOnItemViewClickedListener() { 233 return mOnItemViewClickedListener; 234 } 235 236 /** 237 * Sets a click listener for the search affordance. 238 * 239 * <p>The presence of a listener will change the visibility of the search 240 * affordance in the title area. When set to non-null, the title area will 241 * contain a call to search action. 242 * 243 * <p>The listener's onClick method will be invoked when the user clicks on 244 * the search action. 245 * 246 * @param listener The listener to invoke when the search affordance is 247 * clicked, or null to hide the search affordance. 248 */ 249 public void setOnSearchClickedListener(View.OnClickListener listener) { 250 mExternalOnSearchClickedListener = listener; 251 if (mTitleView != null) { 252 mTitleView.setOnSearchClickedListener(listener); 253 } 254 } 255 256 /** 257 * Sets the {@link SearchOrbView.Colors} used to draw the search affordance. 258 */ 259 public void setSearchAffordanceColors(SearchOrbView.Colors colors) { 260 mSearchAffordanceColors = colors; 261 mSearchAffordanceColorSet = true; 262 if (mTitleView != null) { 263 mTitleView.setSearchAffordanceColors(mSearchAffordanceColors); 264 } 265 } 266 267 /** 268 * Returns the {@link SearchOrbView.Colors} used to draw the search affordance. 269 */ 270 public SearchOrbView.Colors getSearchAffordanceColors() { 271 if (mSearchAffordanceColorSet) { 272 return mSearchAffordanceColors; 273 } 274 if (mTitleView == null) { 275 throw new IllegalStateException("Fragment views not yet created"); 276 } 277 return mTitleView.getSearchAffordanceColors(); 278 } 279 280 /** 281 * Sets the color used to draw the search affordance. 282 * A default brighter color will be set by the framework. 283 * 284 * @param color The color to use for the search affordance. 285 */ 286 public void setSearchAffordanceColor(int color) { 287 setSearchAffordanceColors(new SearchOrbView.Colors(color)); 288 } 289 290 /** 291 * Returns the color used to draw the search affordance. 292 */ 293 public int getSearchAffordanceColor() { 294 return getSearchAffordanceColors().color; 295 } 296 297 private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener = 298 new BrowseFrameLayout.OnFocusSearchListener() { 299 @Override 300 public View onFocusSearch(View focused, int direction) { 301 if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction); 302 303 final View searchOrbView = mTitleView.getSearchAffordanceView(); 304 if (focused == searchOrbView && ( 305 direction == View.FOCUS_DOWN || direction == View.FOCUS_RIGHT)) { 306 return mGridViewHolder.view; 307 308 } else if (focused != searchOrbView && searchOrbView.getVisibility() == View.VISIBLE 309 && direction == View.FOCUS_UP) { 310 return searchOrbView; 311 312 } else { 313 return null; 314 } 315 } 316 }; 317 318 @Override 319 public View onCreateView(LayoutInflater inflater, ViewGroup container, 320 Bundle savedInstanceState) { 321 ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_vertical_grid_fragment, 322 container, false); 323 324 mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame); 325 mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener); 326 327 mTitleView = (TitleView) root.findViewById(R.id.browse_title_group); 328 mTitleView.setBadgeDrawable(mBadgeDrawable); 329 mTitleView.setTitle(mTitle); 330 if (mSearchAffordanceColorSet) { 331 mTitleView.setSearchAffordanceColors(mSearchAffordanceColors); 332 } 333 if (mExternalOnSearchClickedListener != null) { 334 mTitleView.setOnSearchClickedListener(mExternalOnSearchClickedListener); 335 } 336 337 mSceneWithTitle = sTransitionHelper.createScene(root, new Runnable() { 338 @Override 339 public void run() { 340 mTitleView.setVisibility(View.VISIBLE); 341 } 342 }); 343 mSceneWithoutTitle = sTransitionHelper.createScene(root, new Runnable() { 344 @Override 345 public void run() { 346 mTitleView.setVisibility(View.INVISIBLE); 347 } 348 }); 349 mTitleUpTransition = TitleTransitionHelper.createTransitionTitleUp(sTransitionHelper); 350 mTitleDownTransition = TitleTransitionHelper.createTransitionTitleDown(sTransitionHelper); 351 sTransitionHelper.excludeChildren(mTitleUpTransition, R.id.browse_grid_dock, true); 352 sTransitionHelper.excludeChildren(mTitleDownTransition, R.id.browse_grid_dock, true); 353 354 return root; 355 } 356 357 @Override 358 public void onViewCreated(View view, Bundle savedInstanceState) { 359 ViewGroup gridDock = (ViewGroup) view.findViewById(R.id.browse_grid_dock); 360 mGridViewHolder = mGridPresenter.onCreateViewHolder(gridDock); 361 gridDock.addView(mGridViewHolder.view); 362 363 updateAdapter(); 364 } 365 366 @Override 367 public void onStart() { 368 super.onStart(); 369 mGridViewHolder.getGridView().requestFocus(); 370 } 371 372 @Override 373 public void onDestroyView() { 374 super.onDestroyView(); 375 mGridViewHolder = null; 376 } 377 378 /** 379 * Sets the selected item position. 380 */ 381 public void setSelectedPosition(int position) { 382 mSelectedPosition = position; 383 if(mGridViewHolder != null && mGridViewHolder.getGridView().getAdapter() != null) { 384 mGridViewHolder.getGridView().setSelectedPositionSmooth(position); 385 } 386 } 387 388 private void updateAdapter() { 389 if (mGridViewHolder != null) { 390 mGridPresenter.onBindViewHolder(mGridViewHolder, mAdapter); 391 if (mSelectedPosition != -1) { 392 mGridViewHolder.getGridView().setSelectedPosition(mSelectedPosition); 393 } 394 } 395 } 396} 397