FolderPagedView.java revision 261194387beebaa7927ec4e310274218b651494d
1/** 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.launcher3.folder; 18 19import android.annotation.SuppressLint; 20import android.content.Context; 21import android.util.AttributeSet; 22import android.util.Log; 23import android.view.Gravity; 24import android.view.LayoutInflater; 25import android.view.View; 26import android.view.ViewDebug; 27import android.view.animation.DecelerateInterpolator; 28import android.view.animation.Interpolator; 29import android.view.animation.OvershootInterpolator; 30 31import com.android.launcher3.BubbleTextView; 32import com.android.launcher3.CellLayout; 33import com.android.launcher3.DeviceProfile; 34import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener; 35import com.android.launcher3.FocusIndicatorView; 36import com.android.launcher3.IconCache; 37import com.android.launcher3.InvariantDeviceProfile; 38import com.android.launcher3.ItemInfo; 39import com.android.launcher3.Launcher; 40import com.android.launcher3.LauncherAppState; 41import com.android.launcher3.LauncherModel; 42import com.android.launcher3.PageIndicator; 43import com.android.launcher3.PageIndicator.PageMarkerResources; 44import com.android.launcher3.PagedView; 45import com.android.launcher3.R; 46import com.android.launcher3.ShortcutAndWidgetContainer; 47import com.android.launcher3.ShortcutInfo; 48import com.android.launcher3.Utilities; 49import com.android.launcher3.Workspace.ItemOperator; 50import com.android.launcher3.dragndrop.DragController; 51import com.android.launcher3.util.Thunk; 52 53import java.util.ArrayList; 54import java.util.HashMap; 55import java.util.Iterator; 56import java.util.Map; 57 58public class FolderPagedView extends PagedView { 59 60 private static final String TAG = "FolderPagedView"; 61 62 private static final boolean ALLOW_FOLDER_SCROLL = true; 63 64 private static final int REORDER_ANIMATION_DURATION = 230; 65 private static final int START_VIEW_REORDER_DELAY = 30; 66 private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f; 67 68 private static final int PAGE_INDICATOR_ANIMATION_START_DELAY = 300; 69 private static final int PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY = 150; 70 private static final int PAGE_INDICATOR_ANIMATION_DURATION = 400; 71 72 // This value approximately overshoots to 1.5 times the original size. 73 private static final float PAGE_INDICATOR_OVERSHOOT_TENSION = 4.9f; 74 75 /** 76 * Fraction of the width to scroll when showing the next page hint. 77 */ 78 private static final float SCROLL_HINT_FRACTION = 0.07f; 79 80 private static final int[] sTempPosArray = new int[2]; 81 82 public final boolean mIsRtl; 83 84 private final LayoutInflater mInflater; 85 private final IconCache mIconCache; 86 87 @Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>(); 88 89 @ViewDebug.ExportedProperty(category = "launcher") 90 private final int mMaxCountX; 91 @ViewDebug.ExportedProperty(category = "launcher") 92 private final int mMaxCountY; 93 @ViewDebug.ExportedProperty(category = "launcher") 94 private final int mMaxItemsPerPage; 95 96 private int mAllocatedContentSize; 97 @ViewDebug.ExportedProperty(category = "launcher") 98 private int mGridCountX; 99 @ViewDebug.ExportedProperty(category = "launcher") 100 private int mGridCountY; 101 102 private Folder mFolder; 103 private FocusIndicatorView mFocusIndicatorView; 104 private PagedFolderKeyEventListener mKeyListener; 105 106 private PageIndicator mPageIndicator; 107 108 public FolderPagedView(Context context, AttributeSet attrs) { 109 super(context, attrs); 110 LauncherAppState app = LauncherAppState.getInstance(); 111 112 InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); 113 mMaxCountX = profile.numFolderColumns; 114 mMaxCountY = profile.numFolderRows; 115 116 mMaxItemsPerPage = mMaxCountX * mMaxCountY; 117 118 mInflater = LayoutInflater.from(context); 119 mIconCache = app.getIconCache(); 120 121 mIsRtl = Utilities.isRtl(getResources()); 122 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); 123 124 setEdgeGlowColor(getResources().getColor(R.color.folder_edge_effect_color)); 125 } 126 127 public void setFolder(Folder folder) { 128 mFolder = folder; 129 mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator); 130 mKeyListener = new PagedFolderKeyEventListener(folder); 131 mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator); 132 } 133 134 /** 135 * Sets up the grid size such that {@param count} items can fit in the grid. 136 * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while 137 * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. 138 */ 139 private void setupContentDimensions(int count) { 140 mAllocatedContentSize = count; 141 boolean done; 142 if (count >= mMaxItemsPerPage) { 143 mGridCountX = mMaxCountX; 144 mGridCountY = mMaxCountY; 145 done = true; 146 } else { 147 done = false; 148 } 149 150 while (!done) { 151 int oldCountX = mGridCountX; 152 int oldCountY = mGridCountY; 153 if (mGridCountX * mGridCountY < count) { 154 // Current grid is too small, expand it 155 if ((mGridCountX <= mGridCountY || mGridCountY == mMaxCountY) && mGridCountX < mMaxCountX) { 156 mGridCountX++; 157 } else if (mGridCountY < mMaxCountY) { 158 mGridCountY++; 159 } 160 if (mGridCountY == 0) mGridCountY++; 161 } else if ((mGridCountY - 1) * mGridCountX >= count && mGridCountY >= mGridCountX) { 162 mGridCountY = Math.max(0, mGridCountY - 1); 163 } else if ((mGridCountX - 1) * mGridCountY >= count) { 164 mGridCountX = Math.max(0, mGridCountX - 1); 165 } 166 done = mGridCountX == oldCountX && mGridCountY == oldCountY; 167 } 168 169 // Update grid size 170 for (int i = getPageCount() - 1; i >= 0; i--) { 171 getPageAt(i).setGridSize(mGridCountX, mGridCountY); 172 } 173 } 174 175 /** 176 * Binds items to the layout. 177 * @return list of items that could not be bound, probably because we hit the max size limit. 178 */ 179 public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) { 180 ArrayList<View> icons = new ArrayList<View>(); 181 ArrayList<ShortcutInfo> extra = new ArrayList<ShortcutInfo>(); 182 183 for (ShortcutInfo item : items) { 184 if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) { 185 extra.add(item); 186 } else { 187 icons.add(createNewView(item)); 188 } 189 } 190 arrangeChildren(icons, icons.size(), false); 191 return extra; 192 } 193 194 /** 195 * Create space for a new item at the end, and returns the rank for that item. 196 * Also sets the current page to the last page. 197 */ 198 public int allocateRankForNewItem(ShortcutInfo info) { 199 int rank = getItemCount(); 200 ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder()); 201 views.add(rank, null); 202 arrangeChildren(views, views.size(), false); 203 setCurrentPage(rank / mMaxItemsPerPage); 204 return rank; 205 } 206 207 public View createAndAddViewForRank(ShortcutInfo item, int rank) { 208 View icon = createNewView(item); 209 addViewForRank(icon, item, rank); 210 return icon; 211 } 212 213 /** 214 * Adds the {@param view} to the layout based on {@param rank} and updated the position 215 * related attributes. It assumes that {@param item} is already attached to the view. 216 */ 217 public void addViewForRank(View view, ShortcutInfo item, int rank) { 218 int pagePos = rank % mMaxItemsPerPage; 219 int pageNo = rank / mMaxItemsPerPage; 220 221 item.rank = rank; 222 item.cellX = pagePos % mGridCountX; 223 item.cellY = pagePos / mGridCountX; 224 225 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); 226 lp.cellX = item.cellX; 227 lp.cellY = item.cellY; 228 getPageAt(pageNo).addViewToCellLayout( 229 view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true); 230 } 231 232 @SuppressLint("InflateParams") 233 public View createNewView(ShortcutInfo item) { 234 final BubbleTextView textView = (BubbleTextView) mInflater.inflate( 235 R.layout.folder_application, null, false); 236 textView.applyFromShortcutInfo(item, mIconCache); 237 textView.setOnClickListener(mFolder); 238 textView.setOnLongClickListener(mFolder); 239 textView.setOnFocusChangeListener(mFocusIndicatorView); 240 textView.setOnKeyListener(mKeyListener); 241 242 textView.setLayoutParams(new CellLayout.LayoutParams( 243 item.cellX, item.cellY, item.spanX, item.spanY)); 244 return textView; 245 } 246 247 @Override 248 public CellLayout getPageAt(int index) { 249 return (CellLayout) getChildAt(index); 250 } 251 252 public CellLayout getCurrentCellLayout() { 253 return getPageAt(getNextPage()); 254 } 255 256 private CellLayout createAndAddNewPage() { 257 DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile(); 258 CellLayout page = new CellLayout(getContext()); 259 page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); 260 page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); 261 page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); 262 page.setInvertIfRtl(true); 263 page.setGridSize(mGridCountX, mGridCountY); 264 265 addView(page, -1, generateDefaultLayoutParams()); 266 return page; 267 } 268 269 @Override 270 protected int getChildGap() { 271 return getPaddingLeft() + getPaddingRight(); 272 } 273 274 public void setFixedSize(int width, int height) { 275 width -= (getPaddingLeft() + getPaddingRight()); 276 height -= (getPaddingTop() + getPaddingBottom()); 277 for (int i = getChildCount() - 1; i >= 0; i --) { 278 ((CellLayout) getChildAt(i)).setFixedSize(width, height); 279 } 280 } 281 282 public void removeItem(View v) { 283 for (int i = getChildCount() - 1; i >= 0; i --) { 284 getPageAt(i).removeView(v); 285 } 286 } 287 288 /** 289 * Updates position and rank of all the children in the view. 290 * It essentially removes all views from all the pages and then adds them again in appropriate 291 * page. 292 * 293 * @param list the ordered list of children. 294 * @param itemCount if greater than the total children count, empty spaces are left 295 * at the end, otherwise it is ignored. 296 * 297 */ 298 public void arrangeChildren(ArrayList<View> list, int itemCount) { 299 arrangeChildren(list, itemCount, true); 300 } 301 302 @SuppressLint("RtlHardcoded") 303 private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) { 304 ArrayList<CellLayout> pages = new ArrayList<CellLayout>(); 305 for (int i = 0; i < getChildCount(); i++) { 306 CellLayout page = (CellLayout) getChildAt(i); 307 page.removeAllViews(); 308 pages.add(page); 309 } 310 setupContentDimensions(itemCount); 311 312 Iterator<CellLayout> pageItr = pages.iterator(); 313 CellLayout currentPage = null; 314 315 int position = 0; 316 int newX, newY, rank; 317 318 rank = 0; 319 for (int i = 0; i < itemCount; i++) { 320 View v = list.size() > i ? list.get(i) : null; 321 if (currentPage == null || position >= mMaxItemsPerPage) { 322 // Next page 323 if (pageItr.hasNext()) { 324 currentPage = pageItr.next(); 325 } else { 326 currentPage = createAndAddNewPage(); 327 } 328 position = 0; 329 } 330 331 if (v != null) { 332 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); 333 newX = position % mGridCountX; 334 newY = position / mGridCountX; 335 ItemInfo info = (ItemInfo) v.getTag(); 336 if (info.cellX != newX || info.cellY != newY || info.rank != rank) { 337 info.cellX = newX; 338 info.cellY = newY; 339 info.rank = rank; 340 if (saveChanges) { 341 LauncherModel.addOrMoveItemInDatabase(getContext(), info, 342 mFolder.mInfo.id, 0, info.cellX, info.cellY); 343 } 344 } 345 lp.cellX = info.cellX; 346 lp.cellY = info.cellY; 347 currentPage.addViewToCellLayout( 348 v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); 349 350 if (rank < FolderIcon.NUM_ITEMS_IN_PREVIEW && v instanceof BubbleTextView) { 351 ((BubbleTextView) v).verifyHighRes(); 352 } 353 } 354 355 rank ++; 356 position++; 357 } 358 359 // Remove extra views. 360 boolean removed = false; 361 while (pageItr.hasNext()) { 362 removeView(pageItr.next()); 363 removed = true; 364 } 365 if (removed) { 366 setCurrentPage(0); 367 } 368 369 setEnableOverscroll(getPageCount() > 1); 370 371 // Update footer 372 mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE); 373 // Set the gravity as LEFT or RIGHT instead of START, as START depends on the actual text. 374 mFolder.mFolderName.setGravity(getPageCount() > 1 ? 375 (mIsRtl ? Gravity.RIGHT : Gravity.LEFT) : Gravity.CENTER_HORIZONTAL); 376 } 377 378 public int getDesiredWidth() { 379 return getPageCount() > 0 ? 380 (getPageAt(0).getDesiredWidth() + getPaddingLeft() + getPaddingRight()) : 0; 381 } 382 383 public int getDesiredHeight() { 384 return getPageCount() > 0 ? 385 (getPageAt(0).getDesiredHeight() + getPaddingTop() + getPaddingBottom()) : 0; 386 } 387 388 public int getItemCount() { 389 int lastPageIndex = getChildCount() - 1; 390 if (lastPageIndex < 0) { 391 // If there are no pages, nothing has yet been added to the folder. 392 return 0; 393 } 394 return getPageAt(lastPageIndex).getShortcutsAndWidgets().getChildCount() 395 + lastPageIndex * mMaxItemsPerPage; 396 } 397 398 /** 399 * @return the rank of the cell nearest to the provided pixel position. 400 */ 401 public int findNearestArea(int pixelX, int pixelY) { 402 int pageIndex = getNextPage(); 403 CellLayout page = getPageAt(pageIndex); 404 page.findNearestArea(pixelX, pixelY, 1, 1, sTempPosArray); 405 if (mFolder.isLayoutRtl()) { 406 sTempPosArray[0] = page.getCountX() - sTempPosArray[0] - 1; 407 } 408 return Math.min(mAllocatedContentSize - 1, 409 pageIndex * mMaxItemsPerPage + sTempPosArray[1] * mGridCountX + sTempPosArray[0]); 410 } 411 412 @Override 413 protected PageMarkerResources getPageIndicatorMarker(int pageIndex) { 414 return new PageMarkerResources(R.drawable.ic_pageindicator_current_folder, 415 R.drawable.ic_pageindicator_default_folder); 416 } 417 418 public boolean isFull() { 419 return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage; 420 } 421 422 public View getFirstItem() { 423 if (getChildCount() < 1) { 424 return null; 425 } 426 ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets(); 427 if (mGridCountX > 0) { 428 return currContainer.getChildAt(0, 0); 429 } else { 430 return currContainer.getChildAt(0); 431 } 432 } 433 434 public View getLastItem() { 435 if (getChildCount() < 1) { 436 return null; 437 } 438 ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets(); 439 int lastRank = currContainer.getChildCount() - 1; 440 if (mGridCountX > 0) { 441 return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX); 442 } else { 443 return currContainer.getChildAt(lastRank); 444 } 445 } 446 447 /** 448 * Iterates over all its items in a reading order. 449 * @return the view for which the operator returned true. 450 */ 451 public View iterateOverItems(ItemOperator op) { 452 for (int k = 0 ; k < getChildCount(); k++) { 453 CellLayout page = getPageAt(k); 454 for (int j = 0; j < page.getCountY(); j++) { 455 for (int i = 0; i < page.getCountX(); i++) { 456 View v = page.getChildAt(i, j); 457 if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) { 458 return v; 459 } 460 } 461 } 462 } 463 return null; 464 } 465 466 public String getAccessibilityDescription() { 467 return getContext().getString(R.string.folder_opened, mGridCountX, mGridCountY); 468 } 469 470 /** 471 * Sets the focus on the first visible child. 472 */ 473 public void setFocusOnFirstChild() { 474 View firstChild = getCurrentCellLayout().getChildAt(0, 0); 475 if (firstChild != null) { 476 firstChild.requestFocus(); 477 } 478 } 479 480 @Override 481 protected void notifyPageSwitchListener() { 482 super.notifyPageSwitchListener(); 483 if (mFolder != null) { 484 mFolder.updateTextViewFocus(); 485 } 486 } 487 488 /** 489 * Scrolls the current view by a fraction 490 */ 491 public void showScrollHint(int direction) { 492 float fraction = (direction == DragController.SCROLL_LEFT) ^ mIsRtl 493 ? -SCROLL_HINT_FRACTION : SCROLL_HINT_FRACTION; 494 int hint = (int) (fraction * getWidth()); 495 int scroll = getScrollForPage(getNextPage()) + hint; 496 int delta = scroll - getScrollX(); 497 if (delta != 0) { 498 mScroller.setInterpolator(new DecelerateInterpolator()); 499 mScroller.startScroll(getScrollX(), 0, delta, 0, Folder.SCROLL_HINT_DURATION); 500 invalidate(); 501 } 502 } 503 504 public void clearScrollHint() { 505 if (getScrollX() != getScrollForPage(getNextPage())) { 506 snapToPage(getNextPage()); 507 } 508 } 509 510 /** 511 * Finish animation all the views which are animating across pages 512 */ 513 public void completePendingPageChanges() { 514 if (!mPendingAnimations.isEmpty()) { 515 HashMap<View, Runnable> pendingViews = new HashMap<>(mPendingAnimations); 516 for (Map.Entry<View, Runnable> e : pendingViews.entrySet()) { 517 e.getKey().animate().cancel(); 518 e.getValue().run(); 519 } 520 } 521 } 522 523 public boolean rankOnCurrentPage(int rank) { 524 int p = rank / mMaxItemsPerPage; 525 return p == getNextPage(); 526 } 527 528 @Override 529 protected void onPageBeginMoving() { 530 super.onPageBeginMoving(); 531 getVisiblePages(sTempPosArray); 532 for (int i = sTempPosArray[0]; i <= sTempPosArray[1]; i++) { 533 verifyVisibleHighResIcons(i); 534 } 535 } 536 537 /** 538 * Ensures that all the icons on the given page are of high-res 539 */ 540 public void verifyVisibleHighResIcons(int pageNo) { 541 CellLayout page = getPageAt(pageNo); 542 if (page != null) { 543 ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets(); 544 for (int i = parent.getChildCount() - 1; i >= 0; i--) { 545 ((BubbleTextView) parent.getChildAt(i)).verifyHighRes(); 546 } 547 } 548 } 549 550 public int getAllocatedContentSize() { 551 return mAllocatedContentSize; 552 } 553 554 /** 555 * Reorders the items such that the {@param empty} spot moves to {@param target} 556 */ 557 public void realTimeReorder(int empty, int target) { 558 completePendingPageChanges(); 559 int delay = 0; 560 float delayAmount = START_VIEW_REORDER_DELAY; 561 562 // Animation only happens on the current page. 563 int pageToAnimate = getNextPage(); 564 565 int pageT = target / mMaxItemsPerPage; 566 int pagePosT = target % mMaxItemsPerPage; 567 568 if (pageT != pageToAnimate) { 569 Log.e(TAG, "Cannot animate when the target cell is invisible"); 570 } 571 int pagePosE = empty % mMaxItemsPerPage; 572 int pageE = empty / mMaxItemsPerPage; 573 574 int startPos, endPos; 575 int moveStart, moveEnd; 576 int direction; 577 578 if (target == empty) { 579 // No animation 580 return; 581 } else if (target > empty) { 582 // Items will move backwards to make room for the empty cell. 583 direction = 1; 584 585 // If empty cell is in a different page, move them instantly. 586 if (pageE < pageToAnimate) { 587 moveStart = empty; 588 // Instantly move the first item in the current page. 589 moveEnd = pageToAnimate * mMaxItemsPerPage; 590 // Animate the 2nd item in the current page, as the first item was already moved to 591 // the last page. 592 startPos = 0; 593 } else { 594 moveStart = moveEnd = -1; 595 startPos = pagePosE; 596 } 597 598 endPos = pagePosT; 599 } else { 600 // The items will move forward. 601 direction = -1; 602 603 if (pageE > pageToAnimate) { 604 // Move the items immediately. 605 moveStart = empty; 606 // Instantly move the last item in the current page. 607 moveEnd = (pageToAnimate + 1) * mMaxItemsPerPage - 1; 608 609 // Animations start with the second last item in the page 610 startPos = mMaxItemsPerPage - 1; 611 } else { 612 moveStart = moveEnd = -1; 613 startPos = pagePosE; 614 } 615 616 endPos = pagePosT; 617 } 618 619 // Instant moving views. 620 while (moveStart != moveEnd) { 621 int rankToMove = moveStart + direction; 622 int p = rankToMove / mMaxItemsPerPage; 623 int pagePos = rankToMove % mMaxItemsPerPage; 624 int x = pagePos % mGridCountX; 625 int y = pagePos / mGridCountX; 626 627 final CellLayout page = getPageAt(p); 628 final View v = page.getChildAt(x, y); 629 if (v != null) { 630 if (pageToAnimate != p) { 631 page.removeView(v); 632 addViewForRank(v, (ShortcutInfo) v.getTag(), moveStart); 633 } else { 634 // Do a fake animation before removing it. 635 final int newRank = moveStart; 636 final float oldTranslateX = v.getTranslationX(); 637 638 Runnable endAction = new Runnable() { 639 640 @Override 641 public void run() { 642 mPendingAnimations.remove(v); 643 v.setTranslationX(oldTranslateX); 644 ((CellLayout) v.getParent().getParent()).removeView(v); 645 addViewForRank(v, (ShortcutInfo) v.getTag(), newRank); 646 } 647 }; 648 v.animate() 649 .translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth()) 650 .setDuration(REORDER_ANIMATION_DURATION) 651 .setStartDelay(0) 652 .withEndAction(endAction); 653 mPendingAnimations.put(v, endAction); 654 } 655 } 656 moveStart = rankToMove; 657 } 658 659 if ((endPos - startPos) * direction <= 0) { 660 // No animation 661 return; 662 } 663 664 CellLayout page = getPageAt(pageToAnimate); 665 for (int i = startPos; i != endPos; i += direction) { 666 int nextPos = i + direction; 667 View v = page.getChildAt(nextPos % mGridCountX, nextPos / mGridCountX); 668 if (v != null) { 669 ((ItemInfo) v.getTag()).rank -= direction; 670 } 671 if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX, 672 REORDER_ANIMATION_DURATION, delay, true, true)) { 673 delay += delayAmount; 674 delayAmount *= VIEW_REORDER_DELAY_FACTOR; 675 } 676 } 677 } 678 679 public void setMarkerScale(float scale) { 680 int count = mPageIndicator.getChildCount(); 681 for (int i = 0; i < count; i++) { 682 View marker = mPageIndicator.getChildAt(i); 683 marker.animate().cancel(); 684 marker.setScaleX(scale); 685 marker.setScaleY(scale); 686 } 687 } 688 689 public void animateMarkers() { 690 int count = mPageIndicator.getChildCount(); 691 Interpolator interpolator = new OvershootInterpolator(PAGE_INDICATOR_OVERSHOOT_TENSION); 692 for (int i = 0; i < count; i++) { 693 mPageIndicator.getChildAt(i).animate().scaleX(1).scaleY(1) 694 .setInterpolator(interpolator) 695 .setDuration(PAGE_INDICATOR_ANIMATION_DURATION) 696 .setStartDelay(PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY * i 697 + PAGE_INDICATOR_ANIMATION_START_DELAY); 698 } 699 } 700 701 public int itemsPerPage() { 702 return mMaxItemsPerPage; 703 } 704 705 @Override 706 protected void getEdgeVerticalPostion(int[] pos) { 707 pos[0] = 0; 708 pos[1] = getViewportHeight(); 709 } 710} 711