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