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