AdapterViewAnimator.java revision 8322834a2544a673a35c5d4ad0d5909b3ca37600
1/* 2 * Copyright (C) 2010 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 android.widget; 18 19import java.util.ArrayList; 20import java.util.HashMap; 21import java.util.LinkedList; 22 23import android.animation.PropertyAnimator; 24import android.content.Context; 25import android.content.Intent; 26import android.content.res.TypedArray; 27import android.graphics.Rect; 28import android.os.Handler; 29import android.os.Looper; 30import android.util.AttributeSet; 31import android.util.Log; 32import android.view.View; 33import android.view.ViewGroup; 34import android.view.ViewGroup.LayoutParams; 35import android.view.animation.Animation; 36import android.view.animation.AnimationUtils; 37 38/** 39 * Base class for a {@link AdapterView} that will perform animations 40 * when switching between its views. 41 * 42 * @attr ref android.R.styleable#AdapterViewAnimator_inAnimation 43 * @attr ref android.R.styleable#AdapterViewAnimator_outAnimation 44 * @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView 45 */ 46public abstract class AdapterViewAnimator extends AdapterView<Adapter> 47 implements RemoteViewsAdapter.RemoteAdapterConnectionCallback{ 48 private static final String TAG = "RemoteViewAnimator"; 49 50 /** 51 * The index of the current child, which appears anywhere from the beginning 52 * to the end of the current set of children, as specified by {@link #mActiveOffset} 53 */ 54 int mWhichChild = 0; 55 56 /** 57 * Whether or not the first view(s) should be animated in 58 */ 59 boolean mAnimateFirstTime = true; 60 61 /** 62 * Represents where the in the current window of 63 * views the current <code>mDisplayedChild</code> sits 64 */ 65 int mActiveOffset = 0; 66 67 /** 68 * The number of views that the {@link AdapterViewAnimator} keeps as children at any 69 * given time (not counting views that are pending removal, see {@link #mPreviousViews}). 70 */ 71 int mNumActiveViews = 1; 72 73 /** 74 * Array of the children of the {@link AdapterViewAnimator}. This array 75 * is accessed in a circular fashion 76 */ 77 View[] mActiveViews; 78 79 /** 80 * List of views pending removal from the {@link AdapterViewAnimator} 81 */ 82 ArrayList<View> mPreviousViews; 83 84 /** 85 * The index, relative to the adapter, of the beginning of the window of views 86 */ 87 int mCurrentWindowStart = 0; 88 89 /** 90 * The index, relative to the adapter, of the end of the window of views 91 */ 92 int mCurrentWindowEnd = -1; 93 94 /** 95 * The same as {@link #mCurrentWindowStart}, except when the we have bounded 96 * {@link #mCurrentWindowStart} to be non-negative 97 */ 98 int mCurrentWindowStartUnbounded = 0; 99 100 /** 101 * Indicates whether to treat the adapter to be a circular structure, ie. 102 * the view before 0 is considered to be <code>mAdapter.getCount() - 1</code> 103 * 104 * TODO: this doesn't do anything yet 105 * 106 */ 107 boolean mCycleViews = false; 108 109 /** 110 * Handler to post events to the main thread 111 */ 112 Handler mMainQueue; 113 114 /** 115 * Listens for data changes from the adapter 116 */ 117 AdapterDataSetObserver mDataSetObserver; 118 119 /** 120 * The {@link Adapter} for this {@link AdapterViewAnimator} 121 */ 122 Adapter mAdapter; 123 124 /** 125 * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator} 126 */ 127 RemoteViewsAdapter mRemoteViewsAdapter; 128 129 /** 130 * Specifies whether this is the first time the animator is showing views 131 */ 132 boolean mFirstTime = true; 133 134 /** 135 * TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit. 136 */ 137 Animation mInAnimation; 138 Animation mOutAnimation; 139 private ArrayList<View> mViewsToBringToFront; 140 141 public AdapterViewAnimator(Context context) { 142 super(context); 143 initViewAnimator(context, null); 144 } 145 146 public AdapterViewAnimator(Context context, AttributeSet attrs) { 147 super(context, attrs); 148 149 TypedArray a = context.obtainStyledAttributes(attrs, 150 com.android.internal.R.styleable.ViewAnimator); 151 int resource = a.getResourceId( 152 com.android.internal.R.styleable.ViewAnimator_inAnimation, 0); 153 if (resource > 0) { 154 setInAnimation(context, resource); 155 } 156 157 resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_outAnimation, 0); 158 if (resource > 0) { 159 setOutAnimation(context, resource); 160 } 161 162 boolean flag = a.getBoolean( 163 com.android.internal.R.styleable.ViewAnimator_animateFirstView, true); 164 setAnimateFirstView(flag); 165 166 a.recycle(); 167 168 initViewAnimator(context, attrs); 169 } 170 171 /** 172 * Initialize this {@link AdapterViewAnimator} 173 */ 174 private void initViewAnimator(Context context, AttributeSet attrs) { 175 mMainQueue = new Handler(Looper.myLooper()); 176 mActiveViews = new View[mNumActiveViews]; 177 mPreviousViews = new ArrayList<View>(); 178 mViewsToBringToFront = new ArrayList<View>(); 179 } 180 181 /** 182 * This method is used by subclasses to configure the animator to display the 183 * desired number of views, and specify the offset 184 * 185 * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup} 186 * @param activeOffset This parameter specifies where the current index ({@link mWhichChild}) 187 * sits within the window. For example if activeOffset is 1, and numVisibleViews is 3, 188 * and {@link setDisplayedChild} is called with 10, then the effective window will be 189 * the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the 190 * window would instead contain indexes 10, 11 and 12. 191 */ 192 void configureViewAnimator(int numVisibleViews, int activeOffset) { 193 if (activeOffset > numVisibleViews - 1) { 194 // Throw an exception here. 195 } 196 mNumActiveViews = numVisibleViews; 197 mActiveOffset = activeOffset; 198 mActiveViews = new View[mNumActiveViews]; 199 mPreviousViews.clear(); 200 removeAllViewsInLayout(); 201 mCurrentWindowStart = 0; 202 mCurrentWindowEnd = -1; 203 } 204 205 /** 206 * This class should be overridden by subclasses to customize view transitions within 207 * the set of visible views 208 * 209 * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't 210 * in the window 211 * @param toIndex The relative index within the window that the view is going to, -1 if it is 212 * being removed 213 * @param view The view that is being animated 214 */ 215 void animateViewForTransition(int fromIndex, int toIndex, View view) { 216 PropertyAnimator pa; 217 if (fromIndex == -1) { 218 pa = new PropertyAnimator(400, view, "alpha", 0.0f, 1.0f); 219 pa.start(); 220 } else if (toIndex == -1) { 221 pa = new PropertyAnimator(400, view, "alpha", 1.0f, 0.0f); 222 pa.start(); 223 } 224 } 225 226 /** 227 * Sets which child view will be displayed. 228 * 229 * @param whichChild the index of the child view to display 230 */ 231 public void setDisplayedChild(int whichChild) { 232 if (mAdapter != null) { 233 mWhichChild = whichChild; 234 if (whichChild >= mAdapter.getCount()) { 235 mWhichChild = 0; 236 } else if (whichChild < 0) { 237 mWhichChild = mAdapter.getCount() - 1; 238 } 239 240 boolean hasFocus = getFocusedChild() != null; 241 // This will clear old focus if we had it 242 showOnly(mWhichChild); 243 if (hasFocus) { 244 // Try to retake focus if we had it 245 requestFocus(FOCUS_FORWARD); 246 } 247 } 248 } 249 250 /** 251 * Return default inAnimation. To be overriden by subclasses. 252 */ 253 Animation getDefaultInAnimation() { 254 return null; 255 } 256 257 /** 258 * Return default outAnimation. To be overridden by subclasses. 259 */ 260 Animation getDefaultOutAnimation() { 261 return null; 262 } 263 264 /** 265 * To be overridden by subclasses. This method applies a view / index specific 266 * transform to the child view. 267 * 268 * @param child 269 * @param relativeIndex 270 */ 271 void applyTransformForChildAtIndex(View child, int relativeIndex) { 272 } 273 274 /** 275 * Returns the index of the currently displayed child view. 276 */ 277 public int getDisplayedChild() { 278 return mWhichChild; 279 } 280 281 /** 282 * Manually shows the next child. 283 */ 284 public void showNext() { 285 setDisplayedChild(mWhichChild + 1); 286 } 287 288 /** 289 * Manually shows the previous child. 290 */ 291 public void showPrevious() { 292 setDisplayedChild(mWhichChild - 1); 293 } 294 295 /** 296 * Shows only the specified child. The other displays Views exit the screen, 297 * optionally with the with the {@link #getOutAnimation() out animation} and 298 * the specified child enters the screen, optionally with the 299 * {@link #getInAnimation() in animation}. 300 * 301 * @param childIndex The index of the child to be shown. 302 * @param animate Whether or not to use the in and out animations, defaults 303 * to true. 304 */ 305 void showOnly(int childIndex, boolean animate) { 306 showOnly(childIndex, animate, false); 307 } 308 309 private int modulo(int pos, int size) { 310 return (size + (pos % size)) % size; 311 } 312 313 /** 314 * Get the view at this index relative to the current window's start 315 * 316 * @param relativeIndex Position relative to the current window's start 317 * @return View at this index, null if the index is outside the bounds 318 */ 319 View getViewAtRelativeIndex(int relativeIndex) { 320 if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1) { 321 int index = mCurrentWindowStartUnbounded + relativeIndex; 322 return mActiveViews[modulo(index, mNumActiveViews)]; 323 } 324 return null; 325 } 326 327 private LayoutParams createOrReuseLayoutParams(View v) { 328 final LayoutParams currentLp = (LayoutParams) v.getLayoutParams(); 329 if (currentLp instanceof LayoutParams) { 330 return currentLp; 331 } 332 return new LayoutParams(v); 333 } 334 335 void showOnly(int childIndex, boolean animate, boolean onLayout) { 336 if (mAdapter == null) return; 337 338 for (int i = 0; i < mPreviousViews.size(); i++) { 339 View viewToRemove = mPreviousViews.get(i); 340 viewToRemove.clearAnimation(); 341 // applyTransformForChildAtIndex here just allows for any cleanup 342 // associated with this view that may need to be done by a subclass 343 applyTransformForChildAtIndex(viewToRemove, -1); 344 removeViewInLayout(viewToRemove); 345 } 346 mPreviousViews.clear(); 347 int newWindowStartUnbounded = childIndex - mActiveOffset; 348 int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1; 349 int newWindowStart = Math.max(0, newWindowStartUnbounded); 350 int newWindowEnd = Math.min(mAdapter.getCount(), newWindowEndUnbounded); 351 352 // This section clears out any items that are in our mActiveViews list 353 // but are outside the effective bounds of our window (this is becomes an issue 354 // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or 355 // newWindowEndUnbounded > mAdapter.getCount() - 1 356 for (int i = newWindowStartUnbounded; i < newWindowEndUnbounded; i++) { 357 if (i < newWindowStart || i > newWindowEnd) { 358 int index = modulo(i, mNumActiveViews); 359 if (mActiveViews[index] != null) { 360 View previousView = mActiveViews[index]; 361 mPreviousViews.add(previousView); 362 int previousViewRelativeIndex = modulo(index - mCurrentWindowStart, 363 mNumActiveViews); 364 animateViewForTransition(previousViewRelativeIndex, -1, previousView); 365 } 366 } 367 } 368 369 // If the window has changed 370 if (! (newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) { 371 // Run through the indices in the new range 372 for (int i = newWindowStart; i <= newWindowEnd; i++) { 373 374 int oldRelativeIndex = i - mCurrentWindowStartUnbounded; 375 int newRelativeIndex = i - newWindowStartUnbounded; 376 int index = modulo(i, mNumActiveViews); 377 378 // If this item is in the current window, great, we just need to apply 379 // the transform for it's new relative position in the window, and animate 380 // between it's current and new relative positions 381 if (i >= mCurrentWindowStart && i <= mCurrentWindowEnd) { 382 View view = mActiveViews[index]; 383 applyTransformForChildAtIndex(view, newRelativeIndex); 384 animateViewForTransition(oldRelativeIndex, newRelativeIndex, view); 385 386 // Otherwise this view is new, so first we have to displace the view that's 387 // taking the new view's place within our cache (a circular array) 388 } else { 389 if (mActiveViews[index] != null) { 390 View previousView = mActiveViews[index]; 391 mPreviousViews.add(previousView); 392 int previousViewRelativeIndex = modulo(index - mCurrentWindowStart, 393 mNumActiveViews); 394 animateViewForTransition(previousViewRelativeIndex, -1, previousView); 395 396 if (mCurrentWindowStart > newWindowStart) { 397 mViewsToBringToFront.add(previousView); 398 } 399 } 400 401 // We've cleared a spot for the new view. Get it from the adapter, add it 402 // and apply any transform / animation 403 View newView = mAdapter.getView(i, null, this); 404 if (newView != null) { 405 mActiveViews[index] = newView; 406 addViewInLayout(newView, -1, createOrReuseLayoutParams(newView)); 407 applyTransformForChildAtIndex(newView, newRelativeIndex); 408 animateViewForTransition(-1, newRelativeIndex, newView); 409 } 410 } 411 mActiveViews[index].bringToFront(); 412 } 413 414 for (int i = 0; i < mViewsToBringToFront.size(); i++) { 415 View v = mViewsToBringToFront.get(i); 416 v.bringToFront(); 417 } 418 mViewsToBringToFront.clear(); 419 420 mCurrentWindowStart = newWindowStart; 421 mCurrentWindowEnd = newWindowEnd; 422 mCurrentWindowStartUnbounded = newWindowStartUnbounded; 423 } 424 425 mFirstTime = false; 426 if (!onLayout) { 427 requestLayout(); 428 invalidate(); 429 } else { 430 // If the Adapter tries to layout the current view when we get it using getView 431 // above the layout will end up being ignored since we are currently laying out, so 432 // we post a delayed requestLayout and invalidate 433 mMainQueue.post(new Runnable() { 434 @Override 435 public void run() { 436 requestLayout(); 437 invalidate(); 438 } 439 }); 440 } 441 } 442 443 @Override 444 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 445 boolean dataChanged = mDataChanged; 446 if (dataChanged) { 447 handleDataChanged(); 448 449 // if the data changes, mWhichChild might be out of the bounds of the adapter 450 // in this case, we reset mWhichChild to the beginning 451 if (mWhichChild >= mAdapter.getCount()) 452 mWhichChild = 0; 453 454 showOnly(mWhichChild, true, true); 455 } 456 457 final int childCount = getChildCount(); 458 for (int i = 0; i < childCount; i++) { 459 final View child = getChildAt(i); 460 461 int childRight = mPaddingLeft + child.getMeasuredWidth(); 462 int childBottom = mPaddingTop + child.getMeasuredHeight(); 463 LayoutParams lp = (LayoutParams) child.getLayoutParams(); 464 465 child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset, 466 childRight + lp.horizontalOffset, childBottom + lp.verticalOffset); 467 } 468 mDataChanged = false; 469 } 470 471 @Override 472 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 473 final int count = getChildCount(); 474 475 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 476 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 477 478 for (int i = 0; i < count; i++) { 479 final View child = getChildAt(i); 480 481 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 482 483 lp.width = widthSpecSize - mPaddingLeft - mPaddingRight; 484 lp.height = heightSpecSize - mPaddingTop - mPaddingBottom; 485 486 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, 487 MeasureSpec.EXACTLY); 488 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, 489 MeasureSpec.EXACTLY); 490 491 child.measure(childWidthMeasureSpec, childheightMeasureSpec); 492 } 493 setMeasuredDimension(widthSpecSize, heightSpecSize); 494 } 495 496 /** 497 * Shows only the specified child. The other displays Views exit the screen 498 * with the {@link #getOutAnimation() out animation} and the specified child 499 * enters the screen with the {@link #getInAnimation() in animation}. 500 * 501 * @param childIndex The index of the child to be shown. 502 */ 503 void showOnly(int childIndex) { 504 final boolean animate = (!mFirstTime || mAnimateFirstTime); 505 showOnly(childIndex, animate); 506 } 507 508 /** 509 * Returns the View corresponding to the currently displayed child. 510 * 511 * @return The View currently displayed. 512 * 513 * @see #getDisplayedChild() 514 */ 515 public View getCurrentView() { 516 return getViewAtRelativeIndex(mActiveOffset); 517 } 518 519 /** 520 * Returns the current animation used to animate a View that enters the screen. 521 * 522 * @return An Animation or null if none is set. 523 * 524 * @see #setInAnimation(android.view.animation.Animation) 525 * @see #setInAnimation(android.content.Context, int) 526 */ 527 public Animation getInAnimation() { 528 return mInAnimation; 529 } 530 531 /** 532 * Specifies the animation used to animate a View that enters the screen. 533 * 534 * @param inAnimation The animation started when a View enters the screen. 535 * 536 * @see #getInAnimation() 537 * @see #setInAnimation(android.content.Context, int) 538 */ 539 public void setInAnimation(Animation inAnimation) { 540 mInAnimation = inAnimation; 541 } 542 543 /** 544 * Returns the current animation used to animate a View that exits the screen. 545 * 546 * @return An Animation or null if none is set. 547 * 548 * @see #setOutAnimation(android.view.animation.Animation) 549 * @see #setOutAnimation(android.content.Context, int) 550 */ 551 public Animation getOutAnimation() { 552 return mOutAnimation; 553 } 554 555 /** 556 * Specifies the animation used to animate a View that exit the screen. 557 * 558 * @param outAnimation The animation started when a View exit the screen. 559 * 560 * @see #getOutAnimation() 561 * @see #setOutAnimation(android.content.Context, int) 562 */ 563 public void setOutAnimation(Animation outAnimation) { 564 mOutAnimation = outAnimation; 565 } 566 567 /** 568 * Specifies the animation used to animate a View that enters the screen. 569 * 570 * @param context The application's environment. 571 * @param resourceID The resource id of the animation. 572 * 573 * @see #getInAnimation() 574 * @see #setInAnimation(android.view.animation.Animation) 575 */ 576 public void setInAnimation(Context context, int resourceID) { 577 setInAnimation(AnimationUtils.loadAnimation(context, resourceID)); 578 } 579 580 /** 581 * Specifies the animation used to animate a View that exit the screen. 582 * 583 * @param context The application's environment. 584 * @param resourceID The resource id of the animation. 585 * 586 * @see #getOutAnimation() 587 * @see #setOutAnimation(android.view.animation.Animation) 588 */ 589 public void setOutAnimation(Context context, int resourceID) { 590 setOutAnimation(AnimationUtils.loadAnimation(context, resourceID)); 591 } 592 593 /** 594 * Indicates whether the current View should be animated the first time 595 * the ViewAnimation is displayed. 596 * 597 * @param animate True to animate the current View the first time it is displayed, 598 * false otherwise. 599 */ 600 public void setAnimateFirstView(boolean animate) { 601 mAnimateFirstTime = animate; 602 } 603 604 @Override 605 public int getBaseline() { 606 return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline(); 607 } 608 609 @Override 610 public Adapter getAdapter() { 611 return mAdapter; 612 } 613 614 @Override 615 public void setAdapter(Adapter adapter) { 616 if (mAdapter != null && mDataSetObserver != null) { 617 mAdapter.unregisterDataSetObserver(mDataSetObserver); 618 } 619 620 mAdapter = adapter; 621 622 if (mAdapter != null) { 623 mDataSetObserver = new AdapterDataSetObserver(); 624 mAdapter.registerDataSetObserver(mDataSetObserver); 625 } 626 setFocusable(true); 627 } 628 629 /** 630 * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a 631 * RemoteViewsService through the specified intent. 632 * 633 * @param intent the intent used to identify the RemoteViewsService for the adapter to 634 * connect to. 635 */ 636 @android.view.RemotableViewMethod 637 public void setRemoteViewsAdapter(Intent intent) { 638 mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this); 639 } 640 641 @Override 642 public void setSelection(int position) { 643 setDisplayedChild(position); 644 } 645 646 @Override 647 public View getSelectedView() { 648 return getViewAtRelativeIndex(mActiveOffset); 649 } 650 651 /** 652 * Called back when the adapter connects to the RemoteViewsService. 653 */ 654 public void onRemoteAdapterConnected() { 655 if (mRemoteViewsAdapter != mAdapter) { 656 setAdapter(mRemoteViewsAdapter); 657 } 658 } 659 660 /** 661 * Called back when the adapter disconnects from the RemoteViewsService. 662 */ 663 public void onRemoteAdapterDisconnected() { 664 if (mRemoteViewsAdapter != mAdapter) { 665 mRemoteViewsAdapter = null; 666 setAdapter(mRemoteViewsAdapter); 667 } 668 } 669 670 static class LayoutParams extends ViewGroup.LayoutParams { 671 int horizontalOffset; 672 int verticalOffset; 673 View mView; 674 675 LayoutParams(View view) { 676 super(0, 0); 677 horizontalOffset = 0; 678 verticalOffset = 0; 679 mView = view; 680 } 681 682 LayoutParams(Context c, AttributeSet attrs) { 683 super(c, attrs); 684 horizontalOffset = 0; 685 verticalOffset = 0; 686 } 687 688 void setHorizontalOffset(int newHorizontalOffset) { 689 horizontalOffset = newHorizontalOffset; 690 if (mView != null) { 691 mView.requestLayout(); 692 mView.invalidate(); 693 } 694 } 695 696 private Rect parentRect = new Rect(); 697 void invalidateGlobalRegion(View v, Rect r) { 698 View p = v; 699 boolean firstPass = true; 700 parentRect.set(0, 0, 0, 0); 701 while (p.getParent() != null && p.getParent() instanceof View 702 && !parentRect.contains(r)) { 703 if (!firstPass) r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY()); 704 firstPass = false; 705 p = (View) p.getParent(); 706 parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(), 707 p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY()); 708 } 709 p.invalidate(r.left, r.top, r.right, r.bottom); 710 } 711 712 private Rect invalidateRect = new Rect(); 713 // This is public so that PropertyAnimator can access it 714 public void setVerticalOffset(int newVerticalOffset) { 715 int offsetDelta = newVerticalOffset - verticalOffset; 716 verticalOffset = newVerticalOffset; 717 if (mView != null) { 718 mView.requestLayout(); 719 int top = Math.min(mView.getTop() + offsetDelta, mView.getTop()); 720 int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom()); 721 invalidateRect.set(mView.getLeft(), top, mView.getRight(), bottom); 722 invalidateGlobalRegion(mView, invalidateRect); 723 } 724 } 725 } 726} 727