1 /* 2 * Copyright (C) 2013 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 17 package android.transition; 18 19 import android.animation.Animator; 20 import android.animation.Animator.AnimatorListener; 21 import android.animation.Animator.AnimatorPauseListener; 22 import android.annotation.IntDef; 23 import android.content.Context; 24 import android.content.res.TypedArray; 25 import android.util.AttributeSet; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.view.ViewGroupOverlay; 29 30 import com.android.internal.R; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 35 /** 36 * This transition tracks changes to the visibility of target views in the 37 * start and end scenes. Visibility is determined not just by the 38 * {@link View#setVisibility(int)} state of views, but also whether 39 * views exist in the current view hierarchy. The class is intended to be a 40 * utility for subclasses such as {@link Fade}, which use this visibility 41 * information to determine the specific animations to run when visibility 42 * changes occur. Subclasses should implement one or both of the methods 43 * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}, 44 * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)} or 45 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}, 46 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}. 47 */ 48 public abstract class Visibility extends Transition { 49 50 static final String PROPNAME_VISIBILITY = "android:visibility:visibility"; 51 private static final String PROPNAME_PARENT = "android:visibility:parent"; 52 private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation"; 53 54 /** @hide */ 55 @Retention(RetentionPolicy.SOURCE) 56 @IntDef(flag = true, value = { 57 MODE_IN, 58 MODE_OUT, 59 Fade.IN, 60 Fade.OUT 61 }) 62 @interface VisibilityMode {} 63 64 /** 65 * Mode used in {@link #setMode(int)} to make the transition 66 * operate on targets that are appearing. Maybe be combined with 67 * {@link #MODE_OUT} to target Visibility changes both in and out. 68 */ 69 public static final int MODE_IN = 0x1; 70 71 /** 72 * Mode used in {@link #setMode(int)} to make the transition 73 * operate on targets that are disappearing. Maybe be combined with 74 * {@link #MODE_IN} to target Visibility changes both in and out. 75 */ 76 public static final int MODE_OUT = 0x2; 77 78 private static final String[] sTransitionProperties = { 79 PROPNAME_VISIBILITY, 80 PROPNAME_PARENT, 81 }; 82 83 private static class VisibilityInfo { 84 boolean visibilityChange; 85 boolean fadeIn; 86 int startVisibility; 87 int endVisibility; 88 ViewGroup startParent; 89 ViewGroup endParent; 90 } 91 92 private int mMode = MODE_IN | MODE_OUT; 93 private boolean mSuppressLayout = true; 94 Visibility()95 public Visibility() {} 96 Visibility(Context context, AttributeSet attrs)97 public Visibility(Context context, AttributeSet attrs) { 98 super(context, attrs); 99 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VisibilityTransition); 100 int mode = a.getInt(R.styleable.VisibilityTransition_transitionVisibilityMode, 0); 101 a.recycle(); 102 if (mode != 0) { 103 setMode(mode); 104 } 105 } 106 107 /** 108 * This tells the Visibility transition to suppress layout during the transition and release 109 * the suppression after the transition. 110 * @hide 111 */ setSuppressLayout(boolean suppress)112 public void setSuppressLayout(boolean suppress) { 113 this.mSuppressLayout = suppress; 114 } 115 116 /** 117 * Changes the transition to support appearing and/or disappearing Views, depending 118 * on <code>mode</code>. 119 * 120 * @param mode The behavior supported by this transition, a combination of 121 * {@link #MODE_IN} and {@link #MODE_OUT}. 122 * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode 123 */ setMode(@isibilityMode int mode)124 public void setMode(@VisibilityMode int mode) { 125 if ((mode & ~(MODE_IN | MODE_OUT)) != 0) { 126 throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed"); 127 } 128 mMode = mode; 129 } 130 131 /** 132 * Returns whether appearing and/or disappearing Views are supported. 133 * 134 * Returns whether appearing and/or disappearing Views are supported. A combination of 135 * {@link #MODE_IN} and {@link #MODE_OUT}. 136 * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode 137 */ 138 @VisibilityMode getMode()139 public int getMode() { 140 return mMode; 141 } 142 143 @Override getTransitionProperties()144 public String[] getTransitionProperties() { 145 return sTransitionProperties; 146 } 147 captureValues(TransitionValues transitionValues)148 private void captureValues(TransitionValues transitionValues) { 149 int visibility = transitionValues.view.getVisibility(); 150 transitionValues.values.put(PROPNAME_VISIBILITY, visibility); 151 transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent()); 152 int[] loc = new int[2]; 153 transitionValues.view.getLocationOnScreen(loc); 154 transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc); 155 } 156 157 @Override captureStartValues(TransitionValues transitionValues)158 public void captureStartValues(TransitionValues transitionValues) { 159 captureValues(transitionValues); 160 } 161 162 @Override captureEndValues(TransitionValues transitionValues)163 public void captureEndValues(TransitionValues transitionValues) { 164 captureValues(transitionValues); 165 } 166 167 /** 168 * Returns whether the view is 'visible' according to the given values 169 * object. This is determined by testing the same properties in the values 170 * object that are used to determine whether the object is appearing or 171 * disappearing in the {@link 172 * Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)} 173 * method. This method can be called by, for example, subclasses that want 174 * to know whether the object is visible in the same way that Visibility 175 * determines it for the actual animation. 176 * 177 * @param values The TransitionValues object that holds the information by 178 * which visibility is determined. 179 * @return True if the view reference by <code>values</code> is visible, 180 * false otherwise. 181 */ isVisible(TransitionValues values)182 public boolean isVisible(TransitionValues values) { 183 if (values == null) { 184 return false; 185 } 186 int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY); 187 View parent = (View) values.values.get(PROPNAME_PARENT); 188 189 return visibility == View.VISIBLE && parent != null; 190 } 191 getVisibilityChangeInfo(TransitionValues startValues, TransitionValues endValues)192 private static VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues, 193 TransitionValues endValues) { 194 final VisibilityInfo visInfo = new VisibilityInfo(); 195 visInfo.visibilityChange = false; 196 visInfo.fadeIn = false; 197 if (startValues != null && startValues.values.containsKey(PROPNAME_VISIBILITY)) { 198 visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY); 199 visInfo.startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT); 200 } else { 201 visInfo.startVisibility = -1; 202 visInfo.startParent = null; 203 } 204 if (endValues != null && endValues.values.containsKey(PROPNAME_VISIBILITY)) { 205 visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY); 206 visInfo.endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT); 207 } else { 208 visInfo.endVisibility = -1; 209 visInfo.endParent = null; 210 } 211 if (startValues != null && endValues != null) { 212 if (visInfo.startVisibility == visInfo.endVisibility && 213 visInfo.startParent == visInfo.endParent) { 214 return visInfo; 215 } else { 216 if (visInfo.startVisibility != visInfo.endVisibility) { 217 if (visInfo.startVisibility == View.VISIBLE) { 218 visInfo.fadeIn = false; 219 visInfo.visibilityChange = true; 220 } else if (visInfo.endVisibility == View.VISIBLE) { 221 visInfo.fadeIn = true; 222 visInfo.visibilityChange = true; 223 } 224 // no visibilityChange if going between INVISIBLE and GONE 225 } else if (visInfo.startParent != visInfo.endParent) { 226 if (visInfo.endParent == null) { 227 visInfo.fadeIn = false; 228 visInfo.visibilityChange = true; 229 } else if (visInfo.startParent == null) { 230 visInfo.fadeIn = true; 231 visInfo.visibilityChange = true; 232 } 233 } 234 } 235 } else if (startValues == null && visInfo.endVisibility == View.VISIBLE) { 236 visInfo.fadeIn = true; 237 visInfo.visibilityChange = true; 238 } else if (endValues == null && visInfo.startVisibility == View.VISIBLE) { 239 visInfo.fadeIn = false; 240 visInfo.visibilityChange = true; 241 } 242 return visInfo; 243 } 244 245 @Override createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues)246 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, 247 TransitionValues endValues) { 248 VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues); 249 if (visInfo.visibilityChange 250 && (visInfo.startParent != null || visInfo.endParent != null)) { 251 if (visInfo.fadeIn) { 252 return onAppear(sceneRoot, startValues, visInfo.startVisibility, 253 endValues, visInfo.endVisibility); 254 } else { 255 return onDisappear(sceneRoot, startValues, visInfo.startVisibility, 256 endValues, visInfo.endVisibility 257 ); 258 } 259 } 260 return null; 261 } 262 263 /** 264 * The default implementation of this method calls 265 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}. 266 * Subclasses should override this method or 267 * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}. 268 * if they need to create an Animator when targets appear. 269 * The method should only be called by the Visibility class; it is 270 * not intended to be called from external classes. 271 * 272 * @param sceneRoot The root of the transition hierarchy 273 * @param startValues The target values in the start scene 274 * @param startVisibility The target visibility in the start scene 275 * @param endValues The target values in the end scene 276 * @param endVisibility The target visibility in the end scene 277 * @return An Animator to be started at the appropriate time in the 278 * overall transition for this scene change. A null value means no animation 279 * should be run. 280 */ onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility)281 public Animator onAppear(ViewGroup sceneRoot, 282 TransitionValues startValues, int startVisibility, 283 TransitionValues endValues, int endVisibility) { 284 if ((mMode & MODE_IN) != MODE_IN || endValues == null) { 285 return null; 286 } 287 if (startValues == null) { 288 VisibilityInfo parentVisibilityInfo = null; 289 View endParent = (View) endValues.view.getParent(); 290 TransitionValues startParentValues = getMatchedTransitionValues(endParent, 291 false); 292 TransitionValues endParentValues = getTransitionValues(endParent, false); 293 parentVisibilityInfo = 294 getVisibilityChangeInfo(startParentValues, endParentValues); 295 if (parentVisibilityInfo.visibilityChange) { 296 return null; 297 } 298 } 299 return onAppear(sceneRoot, endValues.view, startValues, endValues); 300 } 301 302 /** 303 * The default implementation of this method returns a null Animator. Subclasses should 304 * override this method to make targets appear with the desired transition. The 305 * method should only be called from 306 * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}. 307 * 308 * @param sceneRoot The root of the transition hierarchy 309 * @param view The View to make appear. This will be in the target scene's View hierarchy and 310 * will be VISIBLE. 311 * @param startValues The target values in the start scene 312 * @param endValues The target values in the end scene 313 * @return An Animator to be started at the appropriate time in the 314 * overall transition for this scene change. A null value means no animation 315 * should be run. 316 */ onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)317 public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, 318 TransitionValues endValues) { 319 return null; 320 } 321 322 /** 323 * Subclasses should override this method or 324 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)} 325 * if they need to create an Animator when targets disappear. 326 * The method should only be called by the Visibility class; it is 327 * not intended to be called from external classes. 328 * <p> 329 * The default implementation of this method attempts to find a View to use to call 330 * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}, 331 * based on the situation of the View in the View hierarchy. For example, 332 * if a View was simply removed from its parent, then the View will be added 333 * into a {@link android.view.ViewGroupOverlay} and passed as the <code>view</code> 334 * parameter in {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}. 335 * If a visible View is changed to be {@link View#GONE} or {@link View#INVISIBLE}, 336 * then it can be used as the <code>view</code> and the visibility will be changed 337 * to {@link View#VISIBLE} for the duration of the animation. However, if a View 338 * is in a hierarchy which is also altering its visibility, the situation can be 339 * more complicated. In general, if a view that is no longer in the hierarchy in 340 * the end scene still has a parent (so its parent hierarchy was removed, but it 341 * was not removed from its parent), then it will be left alone to avoid side-effects from 342 * improperly removing it from its parent. The only exception to this is if 343 * the previous {@link Scene} was {@link Scene#getSceneForLayout(ViewGroup, int, 344 * android.content.Context) created from a layout resource file}, then it is considered 345 * safe to un-parent the starting scene view in order to make it disappear.</p> 346 * 347 * @param sceneRoot The root of the transition hierarchy 348 * @param startValues The target values in the start scene 349 * @param startVisibility The target visibility in the start scene 350 * @param endValues The target values in the end scene 351 * @param endVisibility The target visibility in the end scene 352 * @return An Animator to be started at the appropriate time in the 353 * overall transition for this scene change. A null value means no animation 354 * should be run. 355 */ onDisappear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility)356 public Animator onDisappear(ViewGroup sceneRoot, 357 TransitionValues startValues, int startVisibility, 358 TransitionValues endValues, int endVisibility) { 359 if ((mMode & MODE_OUT) != MODE_OUT) { 360 return null; 361 } 362 363 if (startValues == null) { 364 // startValues(and startView) will never be null for disappear transition. 365 return null; 366 } 367 368 final View startView = startValues.view; 369 final View endView = (endValues != null) ? endValues.view : null; 370 View overlayView = null; 371 View viewToKeep = null; 372 boolean reusingOverlayView = false; 373 374 View savedOverlayView = (View) startView.getTag(R.id.transition_overlay_view_tag); 375 if (savedOverlayView != null) { 376 // we've already created overlay for the start view. 377 // it means that we are applying two visibility 378 // transitions for the same view 379 overlayView = savedOverlayView; 380 reusingOverlayView = true; 381 } else { 382 boolean needOverlayForStartView = false; 383 384 if (endView == null || endView.getParent() == null) { 385 if (endView != null) { 386 // endView was removed from its parent - add it to the overlay 387 overlayView = endView; 388 } else { 389 needOverlayForStartView = true; 390 } 391 } else { 392 // visibility change 393 if (endVisibility == View.INVISIBLE) { 394 viewToKeep = endView; 395 } else { 396 // Becoming GONE 397 if (startView == endView) { 398 viewToKeep = endView; 399 } else { 400 needOverlayForStartView = true; 401 } 402 } 403 } 404 405 if (needOverlayForStartView) { 406 // endView does not exist. Use startView only under certain 407 // conditions, because placing a view in an overlay necessitates 408 // it being removed from its current parent 409 if (startView.getParent() == null) { 410 // no parent - safe to use 411 overlayView = startView; 412 } else if (startView.getParent() instanceof View) { 413 View startParent = (View) startView.getParent(); 414 TransitionValues startParentValues = getTransitionValues(startParent, true); 415 TransitionValues endParentValues = getMatchedTransitionValues(startParent, 416 true); 417 VisibilityInfo parentVisibilityInfo = 418 getVisibilityChangeInfo(startParentValues, endParentValues); 419 if (!parentVisibilityInfo.visibilityChange) { 420 overlayView = TransitionUtils.copyViewImage(sceneRoot, startView, 421 startParent); 422 } else { 423 int id = startParent.getId(); 424 if (startParent.getParent() == null && id != View.NO_ID 425 && sceneRoot.findViewById(id) != null && mCanRemoveViews) { 426 // no parent, but its parent is unparented but the parent 427 // hierarchy has been replaced by a new hierarchy with the same id 428 // and it is safe to un-parent startView 429 overlayView = startView; 430 } else { 431 // TODO: Handle this case as well 432 } 433 } 434 } 435 } 436 } 437 438 if (overlayView != null) { 439 // TODO: Need to do this for general case of adding to overlay 440 final ViewGroupOverlay overlay; 441 if (!reusingOverlayView) { 442 overlay = sceneRoot.getOverlay(); 443 int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION); 444 int screenX = screenLoc[0]; 445 int screenY = screenLoc[1]; 446 int[] loc = new int[2]; 447 sceneRoot.getLocationOnScreen(loc); 448 overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft()); 449 overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop()); 450 overlay.add(overlayView); 451 } else { 452 overlay = null; 453 } 454 Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues); 455 if (!reusingOverlayView) { 456 if (animator == null) { 457 overlay.remove(overlayView); 458 } else { 459 startView.setTagInternal(R.id.transition_overlay_view_tag, overlayView); 460 final View finalOverlayView = overlayView; 461 addListener(new TransitionListenerAdapter() { 462 463 @Override 464 public void onTransitionPause(Transition transition) { 465 overlay.remove(finalOverlayView); 466 } 467 468 @Override 469 public void onTransitionResume(Transition transition) { 470 if (finalOverlayView.getParent() == null) { 471 overlay.add(finalOverlayView); 472 } else { 473 cancel(); 474 } 475 } 476 477 @Override 478 public void onTransitionEnd(Transition transition) { 479 startView.setTagInternal(R.id.transition_overlay_view_tag, null); 480 overlay.remove(finalOverlayView); 481 transition.removeListener(this); 482 } 483 }); 484 } 485 } 486 return animator; 487 } 488 489 if (viewToKeep != null) { 490 int originalVisibility = viewToKeep.getVisibility(); 491 viewToKeep.setTransitionVisibility(View.VISIBLE); 492 Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues); 493 if (animator != null) { 494 DisappearListener disappearListener = new DisappearListener(viewToKeep, 495 endVisibility, mSuppressLayout); 496 animator.addListener(disappearListener); 497 animator.addPauseListener(disappearListener); 498 addListener(disappearListener); 499 } else { 500 viewToKeep.setTransitionVisibility(originalVisibility); 501 } 502 return animator; 503 } 504 return null; 505 } 506 507 @Override isTransitionRequired(TransitionValues startValues, TransitionValues newValues)508 public boolean isTransitionRequired(TransitionValues startValues, TransitionValues newValues) { 509 if (startValues == null && newValues == null) { 510 return false; 511 } 512 if (startValues != null && newValues != null && 513 newValues.values.containsKey(PROPNAME_VISIBILITY) != 514 startValues.values.containsKey(PROPNAME_VISIBILITY)) { 515 // The transition wasn't targeted in either the start or end, so it couldn't 516 // have changed. 517 return false; 518 } 519 VisibilityInfo changeInfo = getVisibilityChangeInfo(startValues, newValues); 520 return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE || 521 changeInfo.endVisibility == View.VISIBLE); 522 } 523 524 /** 525 * The default implementation of this method returns a null Animator. Subclasses should 526 * override this method to make targets disappear with the desired transition. The 527 * method should only be called from 528 * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}. 529 * 530 * @param sceneRoot The root of the transition hierarchy 531 * @param view The View to make disappear. This will be in the target scene's View 532 * hierarchy or in an {@link android.view.ViewGroupOverlay} and will be 533 * VISIBLE. 534 * @param startValues The target values in the start scene 535 * @param endValues The target values in the end scene 536 * @return An Animator to be started at the appropriate time in the 537 * overall transition for this scene change. A null value means no animation 538 * should be run. 539 */ onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)540 public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, 541 TransitionValues endValues) { 542 return null; 543 } 544 545 private static class DisappearListener 546 extends TransitionListenerAdapter implements AnimatorListener, AnimatorPauseListener { 547 private final View mView; 548 private final int mFinalVisibility; 549 private final ViewGroup mParent; 550 private final boolean mSuppressLayout; 551 552 private boolean mLayoutSuppressed; 553 boolean mCanceled = false; 554 DisappearListener(View view, int finalVisibility, boolean suppressLayout)555 public DisappearListener(View view, int finalVisibility, boolean suppressLayout) { 556 this.mView = view; 557 this.mFinalVisibility = finalVisibility; 558 this.mParent = (ViewGroup) view.getParent(); 559 this.mSuppressLayout = suppressLayout; 560 // Prevent a layout from including mView in its calculation. 561 suppressLayout(true); 562 } 563 564 @Override onAnimationPause(Animator animation)565 public void onAnimationPause(Animator animation) { 566 if (!mCanceled) { 567 mView.setTransitionVisibility(mFinalVisibility); 568 } 569 } 570 571 @Override onAnimationResume(Animator animation)572 public void onAnimationResume(Animator animation) { 573 if (!mCanceled) { 574 mView.setTransitionVisibility(View.VISIBLE); 575 } 576 } 577 578 @Override onAnimationCancel(Animator animation)579 public void onAnimationCancel(Animator animation) { 580 mCanceled = true; 581 } 582 583 @Override onAnimationRepeat(Animator animation)584 public void onAnimationRepeat(Animator animation) { 585 } 586 587 @Override onAnimationStart(Animator animation)588 public void onAnimationStart(Animator animation) { 589 } 590 591 @Override onAnimationEnd(Animator animation)592 public void onAnimationEnd(Animator animation) { 593 hideViewWhenNotCanceled(); 594 } 595 596 @Override onTransitionEnd(Transition transition)597 public void onTransitionEnd(Transition transition) { 598 hideViewWhenNotCanceled(); 599 transition.removeListener(this); 600 } 601 602 @Override onTransitionPause(Transition transition)603 public void onTransitionPause(Transition transition) { 604 suppressLayout(false); 605 } 606 607 @Override onTransitionResume(Transition transition)608 public void onTransitionResume(Transition transition) { 609 suppressLayout(true); 610 } 611 hideViewWhenNotCanceled()612 private void hideViewWhenNotCanceled() { 613 if (!mCanceled) { 614 // Recreate the parent's display list in case it includes mView. 615 mView.setTransitionVisibility(mFinalVisibility); 616 if (mParent != null) { 617 mParent.invalidate(); 618 } 619 } 620 // Layout is allowed now that the View is in its final state 621 suppressLayout(false); 622 } 623 suppressLayout(boolean suppress)624 private void suppressLayout(boolean suppress) { 625 if (mSuppressLayout && mLayoutSuppressed != suppress && mParent != null) { 626 mLayoutSuppressed = suppress; 627 mParent.suppressLayout(suppress); 628 } 629 } 630 } 631 } 632