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 */ 14 package androidx.leanback.app; 15 16 import android.animation.TimeAnimator; 17 import android.animation.TimeAnimator.TimeListener; 18 import android.os.Bundle; 19 import android.util.Log; 20 import android.view.LayoutInflater; 21 import android.view.View; 22 import android.view.ViewGroup; 23 import android.view.animation.DecelerateInterpolator; 24 import android.view.animation.Interpolator; 25 26 import androidx.annotation.NonNull; 27 import androidx.annotation.Nullable; 28 import androidx.leanback.R; 29 import androidx.leanback.widget.BaseOnItemViewClickedListener; 30 import androidx.leanback.widget.BaseOnItemViewSelectedListener; 31 import androidx.leanback.widget.HorizontalGridView; 32 import androidx.leanback.widget.ItemBridgeAdapter; 33 import androidx.leanback.widget.ListRowPresenter; 34 import androidx.leanback.widget.ObjectAdapter; 35 import androidx.leanback.widget.OnItemViewClickedListener; 36 import androidx.leanback.widget.OnItemViewSelectedListener; 37 import androidx.leanback.widget.Presenter; 38 import androidx.leanback.widget.PresenterSelector; 39 import androidx.leanback.widget.RowPresenter; 40 import androidx.leanback.widget.VerticalGridView; 41 import androidx.leanback.widget.ViewHolderTask; 42 import androidx.recyclerview.widget.RecyclerView; 43 44 import java.util.ArrayList; 45 46 /** 47 * An ordered set of rows of leanback widgets. 48 * <p> 49 * A RowsSupportFragment renders the elements of its 50 * {@link androidx.leanback.widget.ObjectAdapter} as a set 51 * of rows in a vertical list. The Adapter's {@link PresenterSelector} must maintain subclasses 52 * of {@link RowPresenter}. 53 * </p> 54 */ 55 public class RowsSupportFragment extends BaseRowSupportFragment implements 56 BrowseSupportFragment.MainFragmentRowsAdapterProvider, 57 BrowseSupportFragment.MainFragmentAdapterProvider { 58 59 private MainFragmentAdapter mMainFragmentAdapter; 60 private MainFragmentRowsAdapter mMainFragmentRowsAdapter; 61 62 @Override getMainFragmentAdapter()63 public BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter() { 64 if (mMainFragmentAdapter == null) { 65 mMainFragmentAdapter = new MainFragmentAdapter(this); 66 } 67 return mMainFragmentAdapter; 68 } 69 70 @Override getMainFragmentRowsAdapter()71 public BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter() { 72 if (mMainFragmentRowsAdapter == null) { 73 mMainFragmentRowsAdapter = new MainFragmentRowsAdapter(this); 74 } 75 return mMainFragmentRowsAdapter; 76 } 77 78 /** 79 * Internal helper class that manages row select animation and apply a default 80 * dim to each row. 81 */ 82 final class RowViewHolderExtra implements TimeListener { 83 final RowPresenter mRowPresenter; 84 final Presenter.ViewHolder mRowViewHolder; 85 86 final TimeAnimator mSelectAnimator = new TimeAnimator(); 87 88 int mSelectAnimatorDurationInUse; 89 Interpolator mSelectAnimatorInterpolatorInUse; 90 float mSelectLevelAnimStart; 91 float mSelectLevelAnimDelta; 92 RowViewHolderExtra(ItemBridgeAdapter.ViewHolder ibvh)93 RowViewHolderExtra(ItemBridgeAdapter.ViewHolder ibvh) { 94 mRowPresenter = (RowPresenter) ibvh.getPresenter(); 95 mRowViewHolder = ibvh.getViewHolder(); 96 mSelectAnimator.setTimeListener(this); 97 } 98 99 @Override onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime)100 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { 101 if (mSelectAnimator.isRunning()) { 102 updateSelect(totalTime, deltaTime); 103 } 104 } 105 updateSelect(long totalTime, long deltaTime)106 void updateSelect(long totalTime, long deltaTime) { 107 float fraction; 108 if (totalTime >= mSelectAnimatorDurationInUse) { 109 fraction = 1; 110 mSelectAnimator.end(); 111 } else { 112 fraction = (float) (totalTime / (double) mSelectAnimatorDurationInUse); 113 } 114 if (mSelectAnimatorInterpolatorInUse != null) { 115 fraction = mSelectAnimatorInterpolatorInUse.getInterpolation(fraction); 116 } 117 float level = mSelectLevelAnimStart + fraction * mSelectLevelAnimDelta; 118 mRowPresenter.setSelectLevel(mRowViewHolder, level); 119 } 120 animateSelect(boolean select, boolean immediate)121 void animateSelect(boolean select, boolean immediate) { 122 mSelectAnimator.end(); 123 final float end = select ? 1 : 0; 124 if (immediate) { 125 mRowPresenter.setSelectLevel(mRowViewHolder, end); 126 } else if (mRowPresenter.getSelectLevel(mRowViewHolder) != end) { 127 mSelectAnimatorDurationInUse = mSelectAnimatorDuration; 128 mSelectAnimatorInterpolatorInUse = mSelectAnimatorInterpolator; 129 mSelectLevelAnimStart = mRowPresenter.getSelectLevel(mRowViewHolder); 130 mSelectLevelAnimDelta = end - mSelectLevelAnimStart; 131 mSelectAnimator.start(); 132 } 133 } 134 135 } 136 137 static final String TAG = "RowsSupportFragment"; 138 static final boolean DEBUG = false; 139 static final int ALIGN_TOP_NOT_SET = Integer.MIN_VALUE; 140 141 ItemBridgeAdapter.ViewHolder mSelectedViewHolder; 142 private int mSubPosition; 143 boolean mExpand = true; 144 boolean mViewsCreated; 145 private int mAlignedTop = ALIGN_TOP_NOT_SET; 146 boolean mAfterEntranceTransition = true; 147 boolean mFreezeRows; 148 149 BaseOnItemViewSelectedListener mOnItemViewSelectedListener; 150 BaseOnItemViewClickedListener mOnItemViewClickedListener; 151 152 // Select animation and interpolator are not intended to be 153 // exposed at this moment. They might be synced with vertical scroll 154 // animation later. 155 int mSelectAnimatorDuration; 156 Interpolator mSelectAnimatorInterpolator = new DecelerateInterpolator(2); 157 158 private RecyclerView.RecycledViewPool mRecycledViewPool; 159 private ArrayList<Presenter> mPresenterMapper; 160 161 ItemBridgeAdapter.AdapterListener mExternalAdapterListener; 162 163 @Override findGridViewFromRoot(View view)164 protected VerticalGridView findGridViewFromRoot(View view) { 165 return (VerticalGridView) view.findViewById(R.id.container_list); 166 } 167 168 /** 169 * Sets an item clicked listener on the fragment. 170 * OnItemViewClickedListener will override {@link View.OnClickListener} that 171 * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}. 172 * So in general, developer should choose one of the listeners but not both. 173 */ setOnItemViewClickedListener(BaseOnItemViewClickedListener listener)174 public void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) { 175 mOnItemViewClickedListener = listener; 176 if (mViewsCreated) { 177 throw new IllegalStateException( 178 "Item clicked listener must be set before views are created"); 179 } 180 } 181 182 /** 183 * Returns the item clicked listener. 184 */ getOnItemViewClickedListener()185 public BaseOnItemViewClickedListener getOnItemViewClickedListener() { 186 return mOnItemViewClickedListener; 187 } 188 189 /** 190 * @deprecated use {@link BrowseSupportFragment#enableRowScaling(boolean)} instead. 191 * 192 * @param enable true to enable row scaling 193 */ 194 @Deprecated enableRowScaling(boolean enable)195 public void enableRowScaling(boolean enable) { 196 } 197 198 /** 199 * Set the visibility of titles/hovercard of browse rows. 200 */ setExpand(boolean expand)201 public void setExpand(boolean expand) { 202 mExpand = expand; 203 VerticalGridView listView = getVerticalGridView(); 204 if (listView != null) { 205 final int count = listView.getChildCount(); 206 if (DEBUG) Log.v(TAG, "setExpand " + expand + " count " + count); 207 for (int i = 0; i < count; i++) { 208 View view = listView.getChildAt(i); 209 ItemBridgeAdapter.ViewHolder vh = 210 (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view); 211 setRowViewExpanded(vh, mExpand); 212 } 213 } 214 } 215 216 /** 217 * Sets an item selection listener. 218 */ setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener)219 public void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) { 220 mOnItemViewSelectedListener = listener; 221 VerticalGridView listView = getVerticalGridView(); 222 if (listView != null) { 223 final int count = listView.getChildCount(); 224 for (int i = 0; i < count; i++) { 225 View view = listView.getChildAt(i); 226 ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) 227 listView.getChildViewHolder(view); 228 getRowViewHolder(ibvh).setOnItemViewSelectedListener(mOnItemViewSelectedListener); 229 } 230 } 231 } 232 233 /** 234 * Returns an item selection listener. 235 */ getOnItemViewSelectedListener()236 public BaseOnItemViewSelectedListener getOnItemViewSelectedListener() { 237 return mOnItemViewSelectedListener; 238 } 239 240 @Override onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder, int position, int subposition)241 void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder, 242 int position, int subposition) { 243 if (mSelectedViewHolder != viewHolder || mSubPosition != subposition) { 244 if (DEBUG) Log.v(TAG, "new row selected position " + position + " subposition " 245 + subposition + " view " + viewHolder.itemView); 246 mSubPosition = subposition; 247 if (mSelectedViewHolder != null) { 248 setRowViewSelected(mSelectedViewHolder, false, false); 249 } 250 mSelectedViewHolder = (ItemBridgeAdapter.ViewHolder) viewHolder; 251 if (mSelectedViewHolder != null) { 252 setRowViewSelected(mSelectedViewHolder, true, false); 253 } 254 } 255 // When RowsSupportFragment is embedded inside a page fragment, we want to show 256 // the title view only when we're on the first row or there is no data. 257 if (mMainFragmentAdapter != null) { 258 mMainFragmentAdapter.getFragmentHost().showTitleView(position <= 0); 259 } 260 } 261 262 /** 263 * Get row ViewHolder at adapter position. Returns null if the row object is not in adapter or 264 * the row object has not been bound to a row view. 265 * 266 * @param position Position of row in adapter. 267 * @return Row ViewHolder at a given adapter position. 268 */ getRowViewHolder(int position)269 public RowPresenter.ViewHolder getRowViewHolder(int position) { 270 VerticalGridView verticalView = getVerticalGridView(); 271 if (verticalView == null) { 272 return null; 273 } 274 return getRowViewHolder((ItemBridgeAdapter.ViewHolder) 275 verticalView.findViewHolderForAdapterPosition(position)); 276 } 277 278 @Override getLayoutResourceId()279 int getLayoutResourceId() { 280 return R.layout.lb_rows_fragment; 281 } 282 283 @Override onCreate(Bundle savedInstanceState)284 public void onCreate(Bundle savedInstanceState) { 285 super.onCreate(savedInstanceState); 286 mSelectAnimatorDuration = getResources().getInteger( 287 R.integer.lb_browse_rows_anim_duration); 288 } 289 290 @Override onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)291 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 292 if (DEBUG) Log.v(TAG, "onViewCreated"); 293 super.onViewCreated(view, savedInstanceState); 294 // Align the top edge of child with id row_content. 295 // Need set this for directly using RowsSupportFragment. 296 getVerticalGridView().setItemAlignmentViewId(R.id.row_content); 297 getVerticalGridView().setSaveChildrenPolicy(VerticalGridView.SAVE_LIMITED_CHILD); 298 299 setAlignment(mAlignedTop); 300 301 mRecycledViewPool = null; 302 mPresenterMapper = null; 303 if (mMainFragmentAdapter != null) { 304 mMainFragmentAdapter.getFragmentHost().notifyViewCreated(mMainFragmentAdapter); 305 } 306 307 } 308 309 @Override onDestroyView()310 public void onDestroyView() { 311 mViewsCreated = false; 312 super.onDestroyView(); 313 } 314 setExternalAdapterListener(ItemBridgeAdapter.AdapterListener listener)315 void setExternalAdapterListener(ItemBridgeAdapter.AdapterListener listener) { 316 mExternalAdapterListener = listener; 317 } 318 setRowViewExpanded(ItemBridgeAdapter.ViewHolder vh, boolean expanded)319 static void setRowViewExpanded(ItemBridgeAdapter.ViewHolder vh, boolean expanded) { 320 ((RowPresenter) vh.getPresenter()).setRowViewExpanded(vh.getViewHolder(), expanded); 321 } 322 setRowViewSelected(ItemBridgeAdapter.ViewHolder vh, boolean selected, boolean immediate)323 static void setRowViewSelected(ItemBridgeAdapter.ViewHolder vh, boolean selected, 324 boolean immediate) { 325 RowViewHolderExtra extra = (RowViewHolderExtra) vh.getExtraObject(); 326 extra.animateSelect(selected, immediate); 327 ((RowPresenter) vh.getPresenter()).setRowViewSelected(vh.getViewHolder(), selected); 328 } 329 330 private final ItemBridgeAdapter.AdapterListener mBridgeAdapterListener = 331 new ItemBridgeAdapter.AdapterListener() { 332 @Override 333 public void onAddPresenter(Presenter presenter, int type) { 334 if (mExternalAdapterListener != null) { 335 mExternalAdapterListener.onAddPresenter(presenter, type); 336 } 337 } 338 339 @Override 340 public void onCreate(ItemBridgeAdapter.ViewHolder vh) { 341 VerticalGridView listView = getVerticalGridView(); 342 if (listView != null) { 343 // set clip children false for slide animation 344 listView.setClipChildren(false); 345 } 346 setupSharedViewPool(vh); 347 mViewsCreated = true; 348 vh.setExtraObject(new RowViewHolderExtra(vh)); 349 // selected state is initialized to false, then driven by grid view onChildSelected 350 // events. When there is rebind, grid view fires onChildSelected event properly. 351 // So we don't need do anything special later in onBind or onAttachedToWindow. 352 setRowViewSelected(vh, false, true); 353 if (mExternalAdapterListener != null) { 354 mExternalAdapterListener.onCreate(vh); 355 } 356 RowPresenter rowPresenter = (RowPresenter) vh.getPresenter(); 357 RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder()); 358 rowVh.setOnItemViewSelectedListener(mOnItemViewSelectedListener); 359 rowVh.setOnItemViewClickedListener(mOnItemViewClickedListener); 360 } 361 362 @Override 363 public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) { 364 if (DEBUG) Log.v(TAG, "onAttachToWindow"); 365 // All views share the same mExpand value. When we attach a view to grid view, 366 // we should make sure it pick up the latest mExpand value we set early on other 367 // attached views. For no-structure-change update, the view is rebound to new data, 368 // but again it should use the unchanged mExpand value, so we don't need do any 369 // thing in onBind. 370 setRowViewExpanded(vh, mExpand); 371 RowPresenter rowPresenter = (RowPresenter) vh.getPresenter(); 372 RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder()); 373 rowPresenter.setEntranceTransitionState(rowVh, mAfterEntranceTransition); 374 375 // freeze the rows attached after RowsSupportFragment#freezeRows() is called 376 rowPresenter.freeze(rowVh, mFreezeRows); 377 378 if (mExternalAdapterListener != null) { 379 mExternalAdapterListener.onAttachedToWindow(vh); 380 } 381 } 382 383 @Override 384 public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) { 385 if (mSelectedViewHolder == vh) { 386 setRowViewSelected(mSelectedViewHolder, false, true); 387 mSelectedViewHolder = null; 388 } 389 if (mExternalAdapterListener != null) { 390 mExternalAdapterListener.onDetachedFromWindow(vh); 391 } 392 } 393 394 @Override 395 public void onBind(ItemBridgeAdapter.ViewHolder vh) { 396 if (mExternalAdapterListener != null) { 397 mExternalAdapterListener.onBind(vh); 398 } 399 } 400 401 @Override 402 public void onUnbind(ItemBridgeAdapter.ViewHolder vh) { 403 setRowViewSelected(vh, false, true); 404 if (mExternalAdapterListener != null) { 405 mExternalAdapterListener.onUnbind(vh); 406 } 407 } 408 }; 409 setupSharedViewPool(ItemBridgeAdapter.ViewHolder bridgeVh)410 void setupSharedViewPool(ItemBridgeAdapter.ViewHolder bridgeVh) { 411 RowPresenter rowPresenter = (RowPresenter) bridgeVh.getPresenter(); 412 RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(bridgeVh.getViewHolder()); 413 414 if (rowVh instanceof ListRowPresenter.ViewHolder) { 415 HorizontalGridView view = ((ListRowPresenter.ViewHolder) rowVh).getGridView(); 416 // Recycled view pool is shared between all list rows 417 if (mRecycledViewPool == null) { 418 mRecycledViewPool = view.getRecycledViewPool(); 419 } else { 420 view.setRecycledViewPool(mRecycledViewPool); 421 } 422 423 ItemBridgeAdapter bridgeAdapter = 424 ((ListRowPresenter.ViewHolder) rowVh).getBridgeAdapter(); 425 if (mPresenterMapper == null) { 426 mPresenterMapper = bridgeAdapter.getPresenterMapper(); 427 } else { 428 bridgeAdapter.setPresenterMapper(mPresenterMapper); 429 } 430 } 431 } 432 433 @Override updateAdapter()434 void updateAdapter() { 435 super.updateAdapter(); 436 mSelectedViewHolder = null; 437 mViewsCreated = false; 438 439 ItemBridgeAdapter adapter = getBridgeAdapter(); 440 if (adapter != null) { 441 adapter.setAdapterListener(mBridgeAdapterListener); 442 } 443 } 444 445 @Override onTransitionPrepare()446 public boolean onTransitionPrepare() { 447 boolean prepared = super.onTransitionPrepare(); 448 if (prepared) { 449 freezeRows(true); 450 } 451 return prepared; 452 } 453 454 @Override onTransitionEnd()455 public void onTransitionEnd() { 456 super.onTransitionEnd(); 457 freezeRows(false); 458 } 459 freezeRows(boolean freeze)460 private void freezeRows(boolean freeze) { 461 mFreezeRows = freeze; 462 VerticalGridView verticalView = getVerticalGridView(); 463 if (verticalView != null) { 464 final int count = verticalView.getChildCount(); 465 for (int i = 0; i < count; i++) { 466 ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) 467 verticalView.getChildViewHolder(verticalView.getChildAt(i)); 468 RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter(); 469 RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder()); 470 rowPresenter.freeze(vh, freeze); 471 } 472 } 473 } 474 475 /** 476 * For rows that willing to participate entrance transition, this function 477 * hide views if afterTransition is true, show views if afterTransition is false. 478 */ setEntranceTransitionState(boolean afterTransition)479 public void setEntranceTransitionState(boolean afterTransition) { 480 mAfterEntranceTransition = afterTransition; 481 VerticalGridView verticalView = getVerticalGridView(); 482 if (verticalView != null) { 483 final int count = verticalView.getChildCount(); 484 for (int i = 0; i < count; i++) { 485 ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) 486 verticalView.getChildViewHolder(verticalView.getChildAt(i)); 487 RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter(); 488 RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder()); 489 rowPresenter.setEntranceTransitionState(vh, mAfterEntranceTransition); 490 } 491 } 492 } 493 494 /** 495 * Selects a Row and perform an optional task on the Row. For example 496 * <code>setSelectedPosition(10, true, new ListRowPresenterSelectItemViewHolderTask(5))</code> 497 * Scroll to 11th row and selects 6th item on that row. The method will be ignored if 498 * RowsSupportFragment has not been created (i.e. before {@link #onCreateView(LayoutInflater, 499 * ViewGroup, Bundle)}). 500 * 501 * @param rowPosition Which row to select. 502 * @param smooth True to scroll to the row, false for no animation. 503 * @param rowHolderTask Task to perform on the Row. 504 */ setSelectedPosition(int rowPosition, boolean smooth, final Presenter.ViewHolderTask rowHolderTask)505 public void setSelectedPosition(int rowPosition, boolean smooth, 506 final Presenter.ViewHolderTask rowHolderTask) { 507 VerticalGridView verticalView = getVerticalGridView(); 508 if (verticalView == null) { 509 return; 510 } 511 ViewHolderTask task = null; 512 if (rowHolderTask != null) { 513 // This task will execute once the scroll completes. Once the scrolling finishes, 514 // we will get a success callback to update selected row position. Since the 515 // update to selected row position happens in a post, we want to ensure that this 516 // gets called after that. 517 task = new ViewHolderTask() { 518 @Override 519 public void run(final RecyclerView.ViewHolder rvh) { 520 rvh.itemView.post(new Runnable() { 521 @Override 522 public void run() { 523 rowHolderTask.run( 524 getRowViewHolder((ItemBridgeAdapter.ViewHolder) rvh)); 525 } 526 }); 527 } 528 }; 529 } 530 531 if (smooth) { 532 verticalView.setSelectedPositionSmooth(rowPosition, task); 533 } else { 534 verticalView.setSelectedPosition(rowPosition, task); 535 } 536 } 537 getRowViewHolder(ItemBridgeAdapter.ViewHolder ibvh)538 static RowPresenter.ViewHolder getRowViewHolder(ItemBridgeAdapter.ViewHolder ibvh) { 539 if (ibvh == null) { 540 return null; 541 } 542 RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter(); 543 return rowPresenter.getRowViewHolder(ibvh.getViewHolder()); 544 } 545 isScrolling()546 public boolean isScrolling() { 547 if (getVerticalGridView() == null) { 548 return false; 549 } 550 return getVerticalGridView().getScrollState() != HorizontalGridView.SCROLL_STATE_IDLE; 551 } 552 553 @Override setAlignment(int windowAlignOffsetFromTop)554 public void setAlignment(int windowAlignOffsetFromTop) { 555 if (windowAlignOffsetFromTop == ALIGN_TOP_NOT_SET) { 556 return; 557 } 558 mAlignedTop = windowAlignOffsetFromTop; 559 final VerticalGridView gridView = getVerticalGridView(); 560 561 if (gridView != null) { 562 gridView.setItemAlignmentOffset(0); 563 gridView.setItemAlignmentOffsetPercent( 564 VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED); 565 gridView.setItemAlignmentOffsetWithPadding(true); 566 gridView.setWindowAlignmentOffset(mAlignedTop); 567 // align to a fixed position from top 568 gridView.setWindowAlignmentOffsetPercent( 569 VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED); 570 gridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE); 571 } 572 } 573 574 /** 575 * Find row ViewHolder by position in adapter. 576 * @param position Position of row. 577 * @return ViewHolder of Row. 578 */ findRowViewHolderByPosition(int position)579 public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) { 580 if (mVerticalGridView == null) { 581 return null; 582 } 583 return getRowViewHolder((ItemBridgeAdapter.ViewHolder) mVerticalGridView 584 .findViewHolderForAdapterPosition(position)); 585 } 586 587 public static class MainFragmentAdapter extends BrowseSupportFragment.MainFragmentAdapter<RowsSupportFragment> { 588 MainFragmentAdapter(RowsSupportFragment fragment)589 public MainFragmentAdapter(RowsSupportFragment fragment) { 590 super(fragment); 591 setScalingEnabled(true); 592 } 593 594 @Override isScrolling()595 public boolean isScrolling() { 596 return getFragment().isScrolling(); 597 } 598 599 @Override setExpand(boolean expand)600 public void setExpand(boolean expand) { 601 getFragment().setExpand(expand); 602 } 603 604 @Override setEntranceTransitionState(boolean state)605 public void setEntranceTransitionState(boolean state) { 606 getFragment().setEntranceTransitionState(state); 607 } 608 609 @Override setAlignment(int windowAlignOffsetFromTop)610 public void setAlignment(int windowAlignOffsetFromTop) { 611 getFragment().setAlignment(windowAlignOffsetFromTop); 612 } 613 614 @Override onTransitionPrepare()615 public boolean onTransitionPrepare() { 616 return getFragment().onTransitionPrepare(); 617 } 618 619 @Override onTransitionStart()620 public void onTransitionStart() { 621 getFragment().onTransitionStart(); 622 } 623 624 @Override onTransitionEnd()625 public void onTransitionEnd() { 626 getFragment().onTransitionEnd(); 627 } 628 629 } 630 631 /** 632 * The adapter that RowsSupportFragment implements 633 * BrowseSupportFragment.MainFragmentRowsAdapter. 634 * @see #getMainFragmentRowsAdapter(). 635 */ 636 public static class MainFragmentRowsAdapter 637 extends BrowseSupportFragment.MainFragmentRowsAdapter<RowsSupportFragment> { 638 MainFragmentRowsAdapter(RowsSupportFragment fragment)639 public MainFragmentRowsAdapter(RowsSupportFragment fragment) { 640 super(fragment); 641 } 642 643 @Override setAdapter(ObjectAdapter adapter)644 public void setAdapter(ObjectAdapter adapter) { 645 getFragment().setAdapter(adapter); 646 } 647 648 /** 649 * Sets an item clicked listener on the fragment. 650 */ 651 @Override setOnItemViewClickedListener(OnItemViewClickedListener listener)652 public void setOnItemViewClickedListener(OnItemViewClickedListener listener) { 653 getFragment().setOnItemViewClickedListener(listener); 654 } 655 656 @Override setOnItemViewSelectedListener(OnItemViewSelectedListener listener)657 public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) { 658 getFragment().setOnItemViewSelectedListener(listener); 659 } 660 661 @Override setSelectedPosition(int rowPosition, boolean smooth, final Presenter.ViewHolderTask rowHolderTask)662 public void setSelectedPosition(int rowPosition, 663 boolean smooth, 664 final Presenter.ViewHolderTask rowHolderTask) { 665 getFragment().setSelectedPosition(rowPosition, smooth, rowHolderTask); 666 } 667 668 @Override setSelectedPosition(int rowPosition, boolean smooth)669 public void setSelectedPosition(int rowPosition, boolean smooth) { 670 getFragment().setSelectedPosition(rowPosition, smooth); 671 } 672 673 @Override getSelectedPosition()674 public int getSelectedPosition() { 675 return getFragment().getSelectedPosition(); 676 } 677 678 @Override findRowViewHolderByPosition(int position)679 public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) { 680 return getFragment().findRowViewHolderByPosition(position); 681 } 682 } 683 } 684