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