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