BackStackRecord.java revision 5a8f14de0bb4304f9a3aebdcb35ee0dd607f27f7
1/* 2 * Copyright (C) 2011 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.support.v4.app; 18 19import android.graphics.Rect; 20import android.os.Build; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.support.v4.util.LogWriter; 24import android.support.v4.util.Pair; 25import android.support.v4.util.ArrayMap; 26import android.text.TextUtils; 27import android.util.Log; 28import android.util.SparseArray; 29import android.view.View; 30import android.view.ViewGroup; 31import android.view.ViewTreeObserver; 32 33import java.io.FileDescriptor; 34import java.io.PrintWriter; 35import java.util.ArrayList; 36import java.util.Collection; 37 38final class BackStackState implements Parcelable { 39 final int[] mOps; 40 final int mTransition; 41 final int mTransitionStyle; 42 final String mName; 43 final int mIndex; 44 final int mBreadCrumbTitleRes; 45 final CharSequence mBreadCrumbTitleText; 46 final int mBreadCrumbShortTitleRes; 47 final CharSequence mBreadCrumbShortTitleText; 48 final ArrayList<String> mSharedElementSourceNames; 49 final ArrayList<String> mSharedElementTargetNames; 50 51 public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) { 52 int numRemoved = 0; 53 BackStackRecord.Op op = bse.mHead; 54 while (op != null) { 55 if (op.removed != null) numRemoved += op.removed.size(); 56 op = op.next; 57 } 58 mOps = new int[bse.mNumOp*7 + numRemoved]; 59 60 if (!bse.mAddToBackStack) { 61 throw new IllegalStateException("Not on back stack"); 62 } 63 64 op = bse.mHead; 65 int pos = 0; 66 while (op != null) { 67 mOps[pos++] = op.cmd; 68 mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1; 69 mOps[pos++] = op.enterAnim; 70 mOps[pos++] = op.exitAnim; 71 mOps[pos++] = op.popEnterAnim; 72 mOps[pos++] = op.popExitAnim; 73 if (op.removed != null) { 74 final int N = op.removed.size(); 75 mOps[pos++] = N; 76 for (int i=0; i<N; i++) { 77 mOps[pos++] = op.removed.get(i).mIndex; 78 } 79 } else { 80 mOps[pos++] = 0; 81 } 82 op = op.next; 83 } 84 mTransition = bse.mTransition; 85 mTransitionStyle = bse.mTransitionStyle; 86 mName = bse.mName; 87 mIndex = bse.mIndex; 88 mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes; 89 mBreadCrumbTitleText = bse.mBreadCrumbTitleText; 90 mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes; 91 mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText; 92 mSharedElementSourceNames = bse.mSharedElementSourceNames; 93 mSharedElementTargetNames = bse.mSharedElementTargetNames; 94 } 95 96 public BackStackState(Parcel in) { 97 mOps = in.createIntArray(); 98 mTransition = in.readInt(); 99 mTransitionStyle = in.readInt(); 100 mName = in.readString(); 101 mIndex = in.readInt(); 102 mBreadCrumbTitleRes = in.readInt(); 103 mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 104 mBreadCrumbShortTitleRes = in.readInt(); 105 mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 106 mSharedElementSourceNames = in.createStringArrayList(); 107 mSharedElementTargetNames = in.createStringArrayList(); 108 } 109 110 public BackStackRecord instantiate(FragmentManagerImpl fm) { 111 BackStackRecord bse = new BackStackRecord(fm); 112 int pos = 0; 113 int num = 0; 114 while (pos < mOps.length) { 115 BackStackRecord.Op op = new BackStackRecord.Op(); 116 op.cmd = mOps[pos++]; 117 if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, 118 "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]); 119 int findex = mOps[pos++]; 120 if (findex >= 0) { 121 Fragment f = fm.mActive.get(findex); 122 op.fragment = f; 123 } else { 124 op.fragment = null; 125 } 126 op.enterAnim = mOps[pos++]; 127 op.exitAnim = mOps[pos++]; 128 op.popEnterAnim = mOps[pos++]; 129 op.popExitAnim = mOps[pos++]; 130 final int N = mOps[pos++]; 131 if (N > 0) { 132 op.removed = new ArrayList<Fragment>(N); 133 for (int i=0; i<N; i++) { 134 if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, 135 "Instantiate " + bse + " set remove fragment #" + mOps[pos]); 136 Fragment r = fm.mActive.get(mOps[pos++]); 137 op.removed.add(r); 138 } 139 } 140 bse.addOp(op); 141 num++; 142 } 143 bse.mTransition = mTransition; 144 bse.mTransitionStyle = mTransitionStyle; 145 bse.mName = mName; 146 bse.mIndex = mIndex; 147 bse.mAddToBackStack = true; 148 bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes; 149 bse.mBreadCrumbTitleText = mBreadCrumbTitleText; 150 bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes; 151 bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText; 152 bse.mSharedElementSourceNames = mSharedElementSourceNames; 153 bse.mSharedElementTargetNames = mSharedElementTargetNames; 154 bse.bumpBackStackNesting(1); 155 return bse; 156 } 157 158 public int describeContents() { 159 return 0; 160 } 161 162 public void writeToParcel(Parcel dest, int flags) { 163 dest.writeIntArray(mOps); 164 dest.writeInt(mTransition); 165 dest.writeInt(mTransitionStyle); 166 dest.writeString(mName); 167 dest.writeInt(mIndex); 168 dest.writeInt(mBreadCrumbTitleRes); 169 TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0); 170 dest.writeInt(mBreadCrumbShortTitleRes); 171 TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0); 172 dest.writeStringList(mSharedElementSourceNames); 173 dest.writeStringList(mSharedElementTargetNames); 174 } 175 176 public static final Parcelable.Creator<BackStackState> CREATOR 177 = new Parcelable.Creator<BackStackState>() { 178 public BackStackState createFromParcel(Parcel in) { 179 return new BackStackState(in); 180 } 181 182 public BackStackState[] newArray(int size) { 183 return new BackStackState[size]; 184 } 185 }; 186} 187 188/** 189 * @hide Entry of an operation on the fragment back stack. 190 */ 191final class BackStackRecord extends FragmentTransaction implements 192 FragmentManager.BackStackEntry, Runnable { 193 static final String TAG = FragmentManagerImpl.TAG; 194 195 final FragmentManagerImpl mManager; 196 197 static final int OP_NULL = 0; 198 static final int OP_ADD = 1; 199 static final int OP_REPLACE = 2; 200 static final int OP_REMOVE = 3; 201 static final int OP_HIDE = 4; 202 static final int OP_SHOW = 5; 203 static final int OP_DETACH = 6; 204 static final int OP_ATTACH = 7; 205 206 static final class Op { 207 Op next; 208 Op prev; 209 int cmd; 210 Fragment fragment; 211 int enterAnim; 212 int exitAnim; 213 int popEnterAnim; 214 int popExitAnim; 215 ArrayList<Fragment> removed; 216 } 217 218 Op mHead; 219 Op mTail; 220 int mNumOp; 221 int mEnterAnim; 222 int mExitAnim; 223 int mPopEnterAnim; 224 int mPopExitAnim; 225 int mTransition; 226 int mTransitionStyle; 227 boolean mAddToBackStack; 228 boolean mAllowAddToBackStack = true; 229 String mName; 230 boolean mCommitted; 231 int mIndex = -1; 232 233 int mBreadCrumbTitleRes; 234 CharSequence mBreadCrumbTitleText; 235 int mBreadCrumbShortTitleRes; 236 CharSequence mBreadCrumbShortTitleText; 237 238 ArrayList<String> mSharedElementSourceNames; 239 ArrayList<String> mSharedElementTargetNames; 240 241 @Override 242 public String toString() { 243 StringBuilder sb = new StringBuilder(128); 244 sb.append("BackStackEntry{"); 245 sb.append(Integer.toHexString(System.identityHashCode(this))); 246 if (mIndex >= 0) { 247 sb.append(" #"); 248 sb.append(mIndex); 249 } 250 if (mName != null) { 251 sb.append(" "); 252 sb.append(mName); 253 } 254 sb.append("}"); 255 return sb.toString(); 256 } 257 258 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 259 dump(prefix, writer, true); 260 } 261 262 public void dump(String prefix, PrintWriter writer, boolean full) { 263 if (full) { 264 writer.print(prefix); writer.print("mName="); writer.print(mName); 265 writer.print(" mIndex="); writer.print(mIndex); 266 writer.print(" mCommitted="); writer.println(mCommitted); 267 if (mTransition != FragmentTransaction.TRANSIT_NONE) { 268 writer.print(prefix); writer.print("mTransition=#"); 269 writer.print(Integer.toHexString(mTransition)); 270 writer.print(" mTransitionStyle=#"); 271 writer.println(Integer.toHexString(mTransitionStyle)); 272 } 273 if (mEnterAnim != 0 || mExitAnim !=0) { 274 writer.print(prefix); writer.print("mEnterAnim=#"); 275 writer.print(Integer.toHexString(mEnterAnim)); 276 writer.print(" mExitAnim=#"); 277 writer.println(Integer.toHexString(mExitAnim)); 278 } 279 if (mPopEnterAnim != 0 || mPopExitAnim !=0) { 280 writer.print(prefix); writer.print("mPopEnterAnim=#"); 281 writer.print(Integer.toHexString(mPopEnterAnim)); 282 writer.print(" mPopExitAnim=#"); 283 writer.println(Integer.toHexString(mPopExitAnim)); 284 } 285 if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) { 286 writer.print(prefix); writer.print("mBreadCrumbTitleRes=#"); 287 writer.print(Integer.toHexString(mBreadCrumbTitleRes)); 288 writer.print(" mBreadCrumbTitleText="); 289 writer.println(mBreadCrumbTitleText); 290 } 291 if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) { 292 writer.print(prefix); writer.print("mBreadCrumbShortTitleRes=#"); 293 writer.print(Integer.toHexString(mBreadCrumbShortTitleRes)); 294 writer.print(" mBreadCrumbShortTitleText="); 295 writer.println(mBreadCrumbShortTitleText); 296 } 297 } 298 299 if (mHead != null) { 300 writer.print(prefix); writer.println("Operations:"); 301 String innerPrefix = prefix + " "; 302 Op op = mHead; 303 int num = 0; 304 while (op != null) { 305 String cmdStr; 306 switch (op.cmd) { 307 case OP_NULL: cmdStr="NULL"; break; 308 case OP_ADD: cmdStr="ADD"; break; 309 case OP_REPLACE: cmdStr="REPLACE"; break; 310 case OP_REMOVE: cmdStr="REMOVE"; break; 311 case OP_HIDE: cmdStr="HIDE"; break; 312 case OP_SHOW: cmdStr="SHOW"; break; 313 case OP_DETACH: cmdStr="DETACH"; break; 314 case OP_ATTACH: cmdStr="ATTACH"; break; 315 default: cmdStr="cmd=" + op.cmd; break; 316 } 317 writer.print(prefix); writer.print(" Op #"); writer.print(num); 318 writer.print(": "); writer.print(cmdStr); 319 writer.print(" "); writer.println(op.fragment); 320 if (full) { 321 if (op.enterAnim != 0 || op.exitAnim != 0) { 322 writer.print(prefix); writer.print("enterAnim=#"); 323 writer.print(Integer.toHexString(op.enterAnim)); 324 writer.print(" exitAnim=#"); 325 writer.println(Integer.toHexString(op.exitAnim)); 326 } 327 if (op.popEnterAnim != 0 || op.popExitAnim != 0) { 328 writer.print(prefix); writer.print("popEnterAnim=#"); 329 writer.print(Integer.toHexString(op.popEnterAnim)); 330 writer.print(" popExitAnim=#"); 331 writer.println(Integer.toHexString(op.popExitAnim)); 332 } 333 } 334 if (op.removed != null && op.removed.size() > 0) { 335 for (int i=0; i<op.removed.size(); i++) { 336 writer.print(innerPrefix); 337 if (op.removed.size() == 1) { 338 writer.print("Removed: "); 339 } else { 340 if (i == 0) { 341 writer.println("Removed:"); 342 } 343 writer.print(innerPrefix); writer.print(" #"); writer.print(i); 344 writer.print(": "); 345 } 346 writer.println(op.removed.get(i)); 347 } 348 } 349 op = op.next; 350 num++; 351 } 352 } 353 } 354 355 public BackStackRecord(FragmentManagerImpl manager) { 356 mManager = manager; 357 } 358 359 public int getId() { 360 return mIndex; 361 } 362 363 public int getBreadCrumbTitleRes() { 364 return mBreadCrumbTitleRes; 365 } 366 367 public int getBreadCrumbShortTitleRes() { 368 return mBreadCrumbShortTitleRes; 369 } 370 371 public CharSequence getBreadCrumbTitle() { 372 if (mBreadCrumbTitleRes != 0) { 373 return mManager.mActivity.getText(mBreadCrumbTitleRes); 374 } 375 return mBreadCrumbTitleText; 376 } 377 378 public CharSequence getBreadCrumbShortTitle() { 379 if (mBreadCrumbShortTitleRes != 0) { 380 return mManager.mActivity.getText(mBreadCrumbShortTitleRes); 381 } 382 return mBreadCrumbShortTitleText; 383 } 384 385 void addOp(Op op) { 386 if (mHead == null) { 387 mHead = mTail = op; 388 } else { 389 op.prev = mTail; 390 mTail.next = op; 391 mTail = op; 392 } 393 op.enterAnim = mEnterAnim; 394 op.exitAnim = mExitAnim; 395 op.popEnterAnim = mPopEnterAnim; 396 op.popExitAnim = mPopExitAnim; 397 mNumOp++; 398 } 399 400 public FragmentTransaction add(Fragment fragment, String tag) { 401 doAddOp(0, fragment, tag, OP_ADD); 402 return this; 403 } 404 405 public FragmentTransaction add(int containerViewId, Fragment fragment) { 406 doAddOp(containerViewId, fragment, null, OP_ADD); 407 return this; 408 } 409 410 public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) { 411 doAddOp(containerViewId, fragment, tag, OP_ADD); 412 return this; 413 } 414 415 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { 416 fragment.mFragmentManager = mManager; 417 418 if (tag != null) { 419 if (fragment.mTag != null && !tag.equals(fragment.mTag)) { 420 throw new IllegalStateException("Can't change tag of fragment " 421 + fragment + ": was " + fragment.mTag 422 + " now " + tag); 423 } 424 fragment.mTag = tag; 425 } 426 427 if (containerViewId != 0) { 428 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) { 429 throw new IllegalStateException("Can't change container ID of fragment " 430 + fragment + ": was " + fragment.mFragmentId 431 + " now " + containerViewId); 432 } 433 fragment.mContainerId = fragment.mFragmentId = containerViewId; 434 } 435 436 Op op = new Op(); 437 op.cmd = opcmd; 438 op.fragment = fragment; 439 addOp(op); 440 } 441 442 public FragmentTransaction replace(int containerViewId, Fragment fragment) { 443 return replace(containerViewId, fragment, null); 444 } 445 446 public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) { 447 if (containerViewId == 0) { 448 throw new IllegalArgumentException("Must use non-zero containerViewId"); 449 } 450 451 doAddOp(containerViewId, fragment, tag, OP_REPLACE); 452 return this; 453 } 454 455 public FragmentTransaction remove(Fragment fragment) { 456 Op op = new Op(); 457 op.cmd = OP_REMOVE; 458 op.fragment = fragment; 459 addOp(op); 460 461 return this; 462 } 463 464 public FragmentTransaction hide(Fragment fragment) { 465 Op op = new Op(); 466 op.cmd = OP_HIDE; 467 op.fragment = fragment; 468 addOp(op); 469 470 return this; 471 } 472 473 public FragmentTransaction show(Fragment fragment) { 474 Op op = new Op(); 475 op.cmd = OP_SHOW; 476 op.fragment = fragment; 477 addOp(op); 478 479 return this; 480 } 481 482 public FragmentTransaction detach(Fragment fragment) { 483 Op op = new Op(); 484 op.cmd = OP_DETACH; 485 op.fragment = fragment; 486 addOp(op); 487 488 return this; 489 } 490 491 public FragmentTransaction attach(Fragment fragment) { 492 Op op = new Op(); 493 op.cmd = OP_ATTACH; 494 op.fragment = fragment; 495 addOp(op); 496 497 return this; 498 } 499 500 public FragmentTransaction setCustomAnimations(int enter, int exit) { 501 return setCustomAnimations(enter, exit, 0, 0); 502 } 503 504 public FragmentTransaction setCustomAnimations(int enter, int exit, 505 int popEnter, int popExit) { 506 mEnterAnim = enter; 507 mExitAnim = exit; 508 mPopEnterAnim = popEnter; 509 mPopExitAnim = popExit; 510 return this; 511 } 512 513 public FragmentTransaction setTransition(int transition) { 514 mTransition = transition; 515 return this; 516 } 517 518 @Override 519 public FragmentTransaction addSharedElement(View sharedElement, String name) { 520 if (Build.VERSION.SDK_INT >= 21) { 521 String transitionName = FragmentTransitionCompat21.getTransitionName(sharedElement); 522 if (transitionName == null) { 523 throw new IllegalArgumentException("Unique transitionNames are required for all" + 524 " sharedElements"); 525 } 526 if (mSharedElementSourceNames == null) { 527 mSharedElementSourceNames = new ArrayList<String>(); 528 mSharedElementTargetNames = new ArrayList<String>(); 529 } 530 531 mSharedElementSourceNames.add(transitionName); 532 mSharedElementTargetNames.add(name); 533 } 534 return this; 535 } 536 537 public FragmentTransaction setTransitionStyle(int styleRes) { 538 mTransitionStyle = styleRes; 539 return this; 540 } 541 542 public FragmentTransaction addToBackStack(String name) { 543 if (!mAllowAddToBackStack) { 544 throw new IllegalStateException( 545 "This FragmentTransaction is not allowed to be added to the back stack."); 546 } 547 mAddToBackStack = true; 548 mName = name; 549 return this; 550 } 551 552 public boolean isAddToBackStackAllowed() { 553 return mAllowAddToBackStack; 554 } 555 556 public FragmentTransaction disallowAddToBackStack() { 557 if (mAddToBackStack) { 558 throw new IllegalStateException( 559 "This transaction is already being added to the back stack"); 560 } 561 mAllowAddToBackStack = false; 562 return this; 563 } 564 565 public FragmentTransaction setBreadCrumbTitle(int res) { 566 mBreadCrumbTitleRes = res; 567 mBreadCrumbTitleText = null; 568 return this; 569 } 570 571 public FragmentTransaction setBreadCrumbTitle(CharSequence text) { 572 mBreadCrumbTitleRes = 0; 573 mBreadCrumbTitleText = text; 574 return this; 575 } 576 577 public FragmentTransaction setBreadCrumbShortTitle(int res) { 578 mBreadCrumbShortTitleRes = res; 579 mBreadCrumbShortTitleText = null; 580 return this; 581 } 582 583 public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) { 584 mBreadCrumbShortTitleRes = 0; 585 mBreadCrumbShortTitleText = text; 586 return this; 587 } 588 589 void bumpBackStackNesting(int amt) { 590 if (!mAddToBackStack) { 591 return; 592 } 593 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this 594 + " by " + amt); 595 Op op = mHead; 596 while (op != null) { 597 if (op.fragment != null) { 598 op.fragment.mBackStackNesting += amt; 599 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " 600 + op.fragment + " to " + op.fragment.mBackStackNesting); 601 } 602 if (op.removed != null) { 603 for (int i=op.removed.size()-1; i>=0; i--) { 604 Fragment r = op.removed.get(i); 605 r.mBackStackNesting += amt; 606 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " 607 + r + " to " + r.mBackStackNesting); 608 } 609 } 610 op = op.next; 611 } 612 } 613 614 public int commit() { 615 return commitInternal(false); 616 } 617 618 public int commitAllowingStateLoss() { 619 return commitInternal(true); 620 } 621 622 int commitInternal(boolean allowStateLoss) { 623 if (mCommitted) throw new IllegalStateException("commit already called"); 624 if (FragmentManagerImpl.DEBUG) { 625 Log.v(TAG, "Commit: " + this); 626 LogWriter logw = new LogWriter(TAG); 627 PrintWriter pw = new PrintWriter(logw); 628 dump(" ", null, pw, null); 629 } 630 mCommitted = true; 631 if (mAddToBackStack) { 632 mIndex = mManager.allocBackStackIndex(this); 633 } else { 634 mIndex = -1; 635 } 636 mManager.enqueueAction(this, allowStateLoss); 637 return mIndex; 638 } 639 640 public void run() { 641 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this); 642 643 if (mAddToBackStack) { 644 if (mIndex < 0) { 645 throw new IllegalStateException("addToBackStack() called after commit()"); 646 } 647 } 648 649 bumpBackStackNesting(1); 650 651 TransitionState state = null; 652 SparseArray<Fragment> firstOutFragments = null; 653 SparseArray<Fragment> lastInFragments = null; 654 if (Build.VERSION.SDK_INT >= 21) { 655 firstOutFragments = new SparseArray<Fragment>(); 656 lastInFragments = new SparseArray<Fragment>(); 657 658 calculateFragments(firstOutFragments, lastInFragments); 659 660 state = beginTransition(firstOutFragments, lastInFragments, false); 661 } 662 663 int transitionStyle = state != null ? 0 : mTransitionStyle; 664 int transition = state != null ? 0 : mTransition; 665 Op op = mHead; 666 while (op != null) { 667 int enterAnim = state != null ? 0 : op.enterAnim; 668 int exitAnim = state != null ? 0 : op.exitAnim; 669 switch (op.cmd) { 670 case OP_ADD: { 671 Fragment f = op.fragment; 672 f.mNextAnim = enterAnim; 673 mManager.addFragment(f, false); 674 } break; 675 case OP_REPLACE: { 676 Fragment f = op.fragment; 677 if (mManager.mAdded != null) { 678 for (int i=0; i<mManager.mAdded.size(); i++) { 679 Fragment old = mManager.mAdded.get(i); 680 if (FragmentManagerImpl.DEBUG) Log.v(TAG, 681 "OP_REPLACE: adding=" + f + " old=" + old); 682 if (f == null || old.mContainerId == f.mContainerId) { 683 if (old == f) { 684 op.fragment = f = null; 685 } else { 686 if (op.removed == null) { 687 op.removed = new ArrayList<Fragment>(); 688 } 689 op.removed.add(old); 690 old.mNextAnim = exitAnim; 691 if (mAddToBackStack) { 692 old.mBackStackNesting += 1; 693 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " 694 + old + " to " + old.mBackStackNesting); 695 } 696 mManager.removeFragment(old, transition, transitionStyle); 697 } 698 } 699 } 700 } 701 if (f != null) { 702 f.mNextAnim = enterAnim; 703 mManager.addFragment(f, false); 704 } 705 } break; 706 case OP_REMOVE: { 707 Fragment f = op.fragment; 708 f.mNextAnim = exitAnim; 709 mManager.removeFragment(f, transition, transitionStyle); 710 } break; 711 case OP_HIDE: { 712 Fragment f = op.fragment; 713 f.mNextAnim = exitAnim; 714 mManager.hideFragment(f, transition, transitionStyle); 715 } break; 716 case OP_SHOW: { 717 Fragment f = op.fragment; 718 f.mNextAnim = enterAnim; 719 mManager.showFragment(f, transition, transitionStyle); 720 } break; 721 case OP_DETACH: { 722 Fragment f = op.fragment; 723 f.mNextAnim = exitAnim; 724 mManager.detachFragment(f, transition, transitionStyle); 725 } break; 726 case OP_ATTACH: { 727 Fragment f = op.fragment; 728 f.mNextAnim = enterAnim; 729 mManager.attachFragment(f, transition, transitionStyle); 730 } break; 731 default: { 732 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 733 } 734 } 735 736 op = op.next; 737 } 738 739 mManager.moveToState(mManager.mCurState, transition, transitionStyle, true); 740 741 if (mAddToBackStack) { 742 mManager.addBackStackState(this); 743 } 744 } 745 746 private static void setFirstOut(SparseArray<Fragment> fragments, Fragment fragment) { 747 if (fragment != null) { 748 int containerId = fragment.mContainerId; 749 if (containerId != 0 && !fragment.isHidden() && fragment.isAdded() && 750 fragment.getView() != null && fragments.get(containerId) == null) { 751 fragments.put(containerId, fragment); 752 } 753 } 754 } 755 756 private void setLastIn(SparseArray<Fragment> fragments, Fragment fragment) { 757 if (fragment != null) { 758 int containerId = fragment.mContainerId; 759 if (containerId != 0) { 760 fragments.put(containerId, fragment); 761 } 762 } 763 } 764 765 /** 766 * Finds the first removed fragment and last added fragments when going forward. 767 * If none of the fragments have transitions, then both lists will be empty. 768 * 769 * @param firstOutFragments The list of first fragments to be removed, keyed on the 770 * container ID. This list will be modified by the method. 771 * @param lastInFragments The list of last fragments to be added, keyed on the 772 * container ID. This list will be modified by the method. 773 */ 774 private void calculateFragments(SparseArray<Fragment> firstOutFragments, 775 SparseArray<Fragment> lastInFragments) { 776 Op op = mHead; 777 while (op != null) { 778 switch (op.cmd) { 779 case OP_ADD: 780 setLastIn(lastInFragments, op.fragment); 781 break; 782 case OP_REPLACE: { 783 Fragment f = op.fragment; 784 if (mManager.mAdded != null) { 785 for (int i = 0; i < mManager.mAdded.size(); i++) { 786 Fragment old = mManager.mAdded.get(i); 787 if (f == null || old.mContainerId == f.mContainerId) { 788 if (old == f) { 789 f = null; 790 } else { 791 setFirstOut(firstOutFragments, old); 792 } 793 } 794 } 795 } 796 setLastIn(lastInFragments, f); 797 break; 798 } 799 case OP_REMOVE: 800 setFirstOut(firstOutFragments, op.fragment); 801 break; 802 case OP_HIDE: 803 setFirstOut(firstOutFragments, op.fragment); 804 break; 805 case OP_SHOW: 806 setLastIn(lastInFragments, op.fragment); 807 break; 808 case OP_DETACH: 809 setFirstOut(firstOutFragments, op.fragment); 810 break; 811 case OP_ATTACH: 812 setLastIn(lastInFragments, op.fragment); 813 break; 814 } 815 816 op = op.next; 817 } 818 } 819 820 /** 821 * Finds the first removed fragment and last added fragments when popping the back stack. 822 * If none of the fragments have transitions, then both lists will be empty. 823 * 824 * @param firstOutFragments The list of first fragments to be removed, keyed on the 825 * container ID. This list will be modified by the method. 826 * @param lastInFragments The list of last fragments to be added, keyed on the 827 * container ID. This list will be modified by the method. 828 */ 829 public void calculateBackFragments(SparseArray<Fragment> firstOutFragments, 830 SparseArray<Fragment> lastInFragments) { 831 Op op = mHead; 832 while (op != null) { 833 switch (op.cmd) { 834 case OP_ADD: 835 setFirstOut(firstOutFragments, op.fragment); 836 break; 837 case OP_REPLACE: 838 if (op.removed != null) { 839 for (int i = op.removed.size() - 1; i >= 0; i--) { 840 setLastIn(lastInFragments, op.removed.get(i)); 841 } 842 } 843 setFirstOut(firstOutFragments, op.fragment); 844 break; 845 case OP_REMOVE: 846 setLastIn(lastInFragments, op.fragment); 847 break; 848 case OP_HIDE: 849 setLastIn(lastInFragments, op.fragment); 850 break; 851 case OP_SHOW: 852 setFirstOut(firstOutFragments, op.fragment); 853 break; 854 case OP_DETACH: 855 setLastIn(lastInFragments, op.fragment); 856 break; 857 case OP_ATTACH: 858 setFirstOut(firstOutFragments, op.fragment); 859 break; 860 } 861 862 op = op.next; 863 } 864 } 865 866 public TransitionState popFromBackStack(boolean doStateMove, TransitionState state, 867 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) { 868 if (FragmentManagerImpl.DEBUG) { 869 Log.v(TAG, "popFromBackStack: " + this); 870 LogWriter logw = new LogWriter(TAG); 871 PrintWriter pw = new PrintWriter(logw); 872 dump(" ", null, pw, null); 873 } 874 875 if (state == null) { 876 if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) { 877 state = beginTransition(firstOutFragments, lastInFragments, true); 878 } 879 } else if (!doStateMove) { 880 setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames); 881 } 882 883 bumpBackStackNesting(-1); 884 885 int transitionStyle = state != null ? 0 : mTransitionStyle; 886 int transition = state != null ? 0 : mTransition; 887 Op op = mTail; 888 while (op != null) { 889 int popEnterAnim = state != null ? 0 : op.popEnterAnim; 890 int popExitAnim= state != null ? 0 : op.popExitAnim; 891 switch (op.cmd) { 892 case OP_ADD: { 893 Fragment f = op.fragment; 894 f.mNextAnim = popExitAnim; 895 mManager.removeFragment(f, 896 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 897 } break; 898 case OP_REPLACE: { 899 Fragment f = op.fragment; 900 if (f != null) { 901 f.mNextAnim = popExitAnim; 902 mManager.removeFragment(f, 903 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 904 } 905 if (op.removed != null) { 906 for (int i=0; i<op.removed.size(); i++) { 907 Fragment old = op.removed.get(i); 908 old.mNextAnim = popEnterAnim; 909 mManager.addFragment(old, false); 910 } 911 } 912 } break; 913 case OP_REMOVE: { 914 Fragment f = op.fragment; 915 f.mNextAnim = popEnterAnim; 916 mManager.addFragment(f, false); 917 } break; 918 case OP_HIDE: { 919 Fragment f = op.fragment; 920 f.mNextAnim = popEnterAnim; 921 mManager.showFragment(f, 922 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 923 } break; 924 case OP_SHOW: { 925 Fragment f = op.fragment; 926 f.mNextAnim = popExitAnim; 927 mManager.hideFragment(f, 928 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 929 } break; 930 case OP_DETACH: { 931 Fragment f = op.fragment; 932 f.mNextAnim = popEnterAnim; 933 mManager.attachFragment(f, 934 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 935 } break; 936 case OP_ATTACH: { 937 Fragment f = op.fragment; 938 f.mNextAnim = popEnterAnim; 939 mManager.detachFragment(f, 940 FragmentManagerImpl.reverseTransit(transition), transitionStyle); 941 } break; 942 default: { 943 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 944 } 945 } 946 947 op = op.prev; 948 } 949 950 if (doStateMove) { 951 mManager.moveToState(mManager.mCurState, 952 FragmentManagerImpl.reverseTransit(transition), transitionStyle, true); 953 state = null; 954 } 955 956 if (mIndex >= 0) { 957 mManager.freeBackStackIndex(mIndex); 958 mIndex = -1; 959 } 960 return state; 961 } 962 963 public String getName() { 964 return mName; 965 } 966 967 public int getTransition() { 968 return mTransition; 969 } 970 971 public int getTransitionStyle() { 972 return mTransitionStyle; 973 } 974 975 public boolean isEmpty() { 976 return mNumOp == 0; 977 } 978 979 /** 980 * When custom fragment transitions are used, this sets up the state for each transition 981 * and begins the transition. A different transition is started for each fragment container 982 * and consists of up to 3 different transitions: the exit transition, a shared element 983 * transition and an enter transition. 984 * 985 * <p>The exit transition operates against the leaf nodes of the first fragment 986 * with a view that was removed. If no such fragment was removed, then no exit 987 * transition is executed. The exit transition comes from the outgoing fragment.</p> 988 * 989 * <p>The enter transition operates against the last fragment that was added. If 990 * that fragment does not have a view or no fragment was added, then no enter 991 * transition is executed. The enter transition comes from the incoming fragment.</p> 992 * 993 * <p>The shared element transition operates against all views and comes either 994 * from the outgoing fragment or the incoming fragment, depending on whether this 995 * is going forward or popping the back stack. When going forward, the incoming 996 * fragment's enter shared element transition is used, but when going back, the 997 * outgoing fragment's return shared element transition is used. Shared element 998 * transitions only operate if there is both an incoming and outgoing fragment.</p> 999 * 1000 * @param firstOutFragments The list of first fragments to be removed, keyed on the 1001 * container ID. 1002 * @param lastInFragments The list of last fragments to be added, keyed on the 1003 * container ID. 1004 * @param isBack true if this is popping the back stack or false if this is a 1005 * forward operation. 1006 * @return The TransitionState used to complete the operation of the transition 1007 * in {@link #setNameOverrides(BackStackRecord.TransitionState, java.util.ArrayList, 1008 * java.util.ArrayList)}. 1009 */ 1010 private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments, 1011 SparseArray<Fragment> lastInFragments, boolean isBack) { 1012 TransitionState state = new TransitionState(); 1013 1014 // Adding a non-existent target view makes sure that the transitions don't target 1015 // any views by default. They'll only target the views we tell add. If we don't 1016 // add any, then no views will be targeted. 1017 state.nonExistentView = new View(mManager.mActivity); 1018 1019 // Go over all leaving fragments. 1020 for (int i = 0; i < firstOutFragments.size(); i++) { 1021 int containerId = firstOutFragments.keyAt(i); 1022 configureTransitions(containerId, state, isBack, firstOutFragments, 1023 lastInFragments); 1024 } 1025 1026 // Now go over all entering fragments that didn't have a leaving fragment. 1027 for (int i = 0; i < lastInFragments.size(); i++) { 1028 int containerId = lastInFragments.keyAt(i); 1029 if (firstOutFragments.get(containerId) == null) { 1030 configureTransitions(containerId, state, isBack, firstOutFragments, 1031 lastInFragments); 1032 } 1033 } 1034 1035 return state; 1036 } 1037 1038 private static Object getEnterTransition(Fragment inFragment, boolean isBack) { 1039 if (inFragment == null) { 1040 return null; 1041 } 1042 return FragmentTransitionCompat21.cloneTransition(isBack ? 1043 inFragment.getReenterTransition() : inFragment.getEnterTransition()); 1044 } 1045 1046 private static Object getExitTransition(Fragment outFragment, boolean isBack) { 1047 if (outFragment == null) { 1048 return null; 1049 } 1050 return FragmentTransitionCompat21.cloneTransition(isBack ? 1051 outFragment.getReturnTransition() : outFragment.getExitTransition()); 1052 } 1053 1054 private static Object getSharedElementTransition(Fragment inFragment, Fragment outFragment, 1055 boolean isBack) { 1056 if (inFragment == null || outFragment == null) { 1057 return null; 1058 } 1059 return FragmentTransitionCompat21.cloneTransition(isBack ? 1060 outFragment.getSharedElementReturnTransition() : 1061 inFragment.getSharedElementEnterTransition()); 1062 } 1063 1064 private static Object captureExitingViews(Object exitTransition, Fragment outFragment, 1065 ArrayList<View> exitingViews, ArrayMap<String, View> namedViews) { 1066 if (exitTransition != null) { 1067 exitTransition = FragmentTransitionCompat21.captureExitingViews(exitTransition, 1068 outFragment.getView(), exitingViews, namedViews); 1069 } 1070 return exitTransition; 1071 } 1072 1073 private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment, 1074 boolean isBack) { 1075 ArrayMap<String, View> namedViews = new ArrayMap<String, View>(); 1076 if (mSharedElementSourceNames != null) { 1077 FragmentTransitionCompat21.findNamedViews(namedViews, outFragment.getView()); 1078 if (isBack) { 1079 namedViews.retainAll(mSharedElementTargetNames); 1080 } else { 1081 namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames, 1082 namedViews); 1083 } 1084 } 1085 1086 if (isBack) { 1087 if (outFragment.mEnterTransitionCallback != null) { 1088 outFragment.mEnterTransitionCallback.onMapSharedElements( 1089 mSharedElementTargetNames, namedViews); 1090 } 1091 setBackNameOverrides(state, namedViews, false); 1092 } else { 1093 if (outFragment.mExitTransitionCallback != null) { 1094 outFragment.mExitTransitionCallback.onMapSharedElements( 1095 mSharedElementTargetNames, namedViews); 1096 } 1097 setNameOverrides(state, namedViews, false); 1098 } 1099 1100 return namedViews; 1101 } 1102 1103 /** 1104 * Configures custom transitions for a specific fragment container. 1105 * 1106 * @param containerId The container ID of the fragments to configure the transition for. 1107 * @param state The Transition State keeping track of the executing transitions. 1108 * @param firstOutFragments The list of first fragments to be removed, keyed on the 1109 * container ID. 1110 * @param lastInFragments The list of last fragments to be added, keyed on the 1111 * container ID. 1112 * @param isBack true if this is popping the back stack or false if this is a 1113 * forward operation. 1114 */ 1115 private void configureTransitions(int containerId, TransitionState state, boolean isBack, 1116 SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) { 1117 ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.findViewById(containerId); 1118 if (sceneRoot != null) { 1119 final Fragment inFragment = lastInFragments.get(containerId); 1120 Fragment outFragment = firstOutFragments.get(containerId); 1121 1122 Object enterTransition = getEnterTransition(inFragment, isBack); 1123 Object sharedElementTransition = getSharedElementTransition(inFragment, outFragment, 1124 isBack); 1125 Object exitTransition = getExitTransition(outFragment, isBack); 1126 if (enterTransition == null && sharedElementTransition == null && 1127 exitTransition == null) { 1128 return; // no transitions! 1129 } 1130 ArrayMap<String, View> namedViews = null; 1131 if (sharedElementTransition != null) { 1132 namedViews = remapSharedElements(state, outFragment, isBack); 1133 1134 // Notify the start of the transition. 1135 SharedElementCallback callback = isBack ? 1136 outFragment.mEnterTransitionCallback : 1137 inFragment.mEnterTransitionCallback; 1138 if (callback != null) { 1139 ArrayList<String> names = new ArrayList<String>(namedViews.keySet()); 1140 ArrayList<View> views = new ArrayList<View>(namedViews.values()); 1141 callback.onSharedElementStart(names, views, null); 1142 } 1143 } 1144 1145 ArrayList<View> exitingViews = new ArrayList<View>(); 1146 exitTransition = captureExitingViews(exitTransition, outFragment, exitingViews, 1147 namedViews); 1148 1149 // Set the epicenter of the exit transition 1150 if (mSharedElementTargetNames != null && namedViews != null) { 1151 View epicenterView = namedViews.get(mSharedElementTargetNames.get(0)); 1152 if (epicenterView != null) { 1153 if (exitTransition != null) { 1154 FragmentTransitionCompat21.setEpicenter(exitTransition, epicenterView); 1155 } 1156 if (sharedElementTransition != null) { 1157 FragmentTransitionCompat21.setEpicenter(sharedElementTransition, 1158 epicenterView); 1159 } 1160 } 1161 } 1162 1163 FragmentTransitionCompat21.ViewRetriever viewRetriever = 1164 new FragmentTransitionCompat21.ViewRetriever() { 1165 @Override 1166 public View getView() { 1167 return inFragment.getView(); 1168 } 1169 }; 1170 1171 if (sharedElementTransition != null) { 1172 prepareSharedElementTransition(state, sceneRoot, sharedElementTransition, 1173 inFragment, outFragment, isBack); 1174 } 1175 1176 ArrayList<View> enteringViews = new ArrayList<View>(); 1177 ArrayMap<String, View> renamedViews = new ArrayMap<String, View>(); 1178 1179 boolean allowOverlap = isBack ? inFragment.getAllowReturnTransitionOverlap() : 1180 inFragment.getAllowEnterTransitionOverlap(); 1181 Object transition = FragmentTransitionCompat21.mergeTransitions(enterTransition, 1182 exitTransition, sharedElementTransition, allowOverlap); 1183 1184 if (transition != null) { 1185 FragmentTransitionCompat21.addTransitionTargets(enterTransition, 1186 sharedElementTransition, sceneRoot, viewRetriever, state.nonExistentView, 1187 state.enteringEpicenterView, state.nameOverrides, enteringViews, 1188 renamedViews); 1189 excludeHiddenFragmentsAfterEnter(sceneRoot, state, containerId, transition); 1190 1191 // We want to exclude hidden views later, so we need a non-null list in the 1192 // transition now. 1193 FragmentTransitionCompat21.excludeTarget(transition, state.nonExistentView, true); 1194 // Now exclude all currently hidden fragments. 1195 excludeHiddenFragments(state, containerId, transition); 1196 1197 FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition); 1198 1199 FragmentTransitionCompat21.cleanupTransitions(sceneRoot, state.nonExistentView, 1200 enterTransition, enteringViews, exitTransition, exitingViews, 1201 transition, state.hiddenFragmentViews, renamedViews); 1202 } 1203 } 1204 } 1205 1206 private void prepareSharedElementTransition(final TransitionState state, final View sceneRoot, 1207 final Object sharedElementTransition, final Fragment inFragment, 1208 final Fragment outFragment, final boolean isBack) { 1209 sceneRoot.getViewTreeObserver().addOnPreDrawListener( 1210 new ViewTreeObserver.OnPreDrawListener() { 1211 @Override 1212 public boolean onPreDraw() { 1213 sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); 1214 1215 if (sharedElementTransition != null) { 1216 ArrayMap<String, View> namedViews = mapSharedElementsIn( 1217 state, isBack, inFragment); 1218 1219 setEpicenterIn(namedViews, state); 1220 1221 callSharedElementEnd(state, inFragment, outFragment, isBack, 1222 namedViews); 1223 } 1224 1225 return true; 1226 } 1227 }); 1228 } 1229 1230 private void callSharedElementEnd(TransitionState state, Fragment inFragment, 1231 Fragment outFragment, boolean isBack, ArrayMap<String, View> namedViews) { 1232 SharedElementCallback sharedElementCallback = isBack ? 1233 outFragment.mEnterTransitionCallback : 1234 inFragment.mEnterTransitionCallback; 1235 if (sharedElementCallback != null) { 1236 ArrayList<String> names = new ArrayList<String>(namedViews.keySet()); 1237 ArrayList<View> views = new ArrayList<View>(namedViews.values()); 1238 sharedElementCallback.onSharedElementEnd(names, views, null); 1239 } 1240 } 1241 1242 private void setEpicenterIn(ArrayMap<String, View> namedViews, TransitionState state) { 1243 if (mSharedElementTargetNames != null && !namedViews.isEmpty()) { 1244 // now we know the epicenter of the entering transition. 1245 View epicenter = namedViews 1246 .get(mSharedElementTargetNames.get(0)); 1247 if (epicenter != null) { 1248 state.enteringEpicenterView.epicenter = epicenter; 1249 } 1250 } 1251 } 1252 1253 private ArrayMap<String, View> mapSharedElementsIn(TransitionState state, 1254 boolean isBack, Fragment inFragment) { 1255 // Now map the shared elements in the incoming fragment 1256 ArrayMap<String, View> namedViews = mapEnteringSharedElements(state, inFragment, isBack); 1257 1258 // remap shared elements and set the name mapping used 1259 // in the shared element transition. 1260 if (isBack) { 1261 if (inFragment.mExitTransitionCallback != null) { 1262 inFragment.mExitTransitionCallback.onMapSharedElements( 1263 mSharedElementTargetNames, namedViews); 1264 } 1265 setBackNameOverrides(state, namedViews, true); 1266 } else { 1267 if (inFragment.mEnterTransitionCallback != null) { 1268 inFragment.mEnterTransitionCallback.onMapSharedElements( 1269 mSharedElementTargetNames, namedViews); 1270 } 1271 setNameOverrides(state, namedViews, true); 1272 } 1273 return namedViews; 1274 } 1275 1276 /** 1277 * Remaps a name-to-View map, substituting different names for keys. 1278 * 1279 * @param inMap A list of keys found in the map, in the order in toGoInMap 1280 * @param toGoInMap A list of keys to use for the new map, in the order of inMap 1281 * @param namedViews The current mapping 1282 * @return A copy of namedViews with the keys coming from toGoInMap. 1283 */ 1284 private static ArrayMap<String, View> remapNames(ArrayList<String> inMap, 1285 ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews) { 1286 if (namedViews.isEmpty()) { 1287 return namedViews; 1288 } 1289 ArrayMap<String, View> remappedViews = new ArrayMap<String, View>(); 1290 int numKeys = inMap.size(); 1291 for (int i = 0; i < numKeys; i++) { 1292 View view = namedViews.get(inMap.get(i)); 1293 if (view != null) { 1294 remappedViews.put(toGoInMap.get(i), view); 1295 } 1296 } 1297 return remappedViews; 1298 } 1299 1300 /** 1301 * Maps shared elements to views in the entering fragment. 1302 * 1303 * @param state The transition State as returned from {@link #beginTransition( 1304 * android.util.SparseArray, android.util.SparseArray, boolean)}. 1305 * @param inFragment The last fragment to be added. 1306 * @param isBack true if this is popping the back stack or false if this is a 1307 * forward operation. 1308 */ 1309 private ArrayMap<String, View> mapEnteringSharedElements(TransitionState state, 1310 Fragment inFragment, boolean isBack) { 1311 ArrayMap<String, View> namedViews = new ArrayMap<String, View>(); 1312 View root = inFragment.getView(); 1313 if (root != null) { 1314 if (mSharedElementSourceNames != null) { 1315 FragmentTransitionCompat21.findNamedViews(namedViews, root); 1316 if (isBack) { 1317 namedViews = remapNames(mSharedElementSourceNames, 1318 mSharedElementTargetNames, namedViews); 1319 } else { 1320 namedViews.retainAll(mSharedElementTargetNames); 1321 } 1322 } 1323 } 1324 return namedViews; 1325 } 1326 1327 private void excludeHiddenFragmentsAfterEnter(final View sceneRoot, final TransitionState state, 1328 final int containerId, final Object transition) { 1329 sceneRoot.getViewTreeObserver().addOnPreDrawListener( 1330 new ViewTreeObserver.OnPreDrawListener() { 1331 public boolean onPreDraw() { 1332 sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); 1333 excludeHiddenFragments(state, containerId, transition); 1334 return true; 1335 } 1336 }); 1337 } 1338 1339 private void excludeHiddenFragments(TransitionState state, int containerId, Object transition) { 1340 if (mManager.mAdded != null) { 1341 for (int i = 0; i < mManager.mAdded.size(); i++) { 1342 Fragment fragment = mManager.mAdded.get(i); 1343 if (fragment.mView != null && fragment.mContainer != null && 1344 fragment.mContainerId == containerId) { 1345 if (fragment.mHidden) { 1346 if (!state.hiddenFragmentViews.contains(fragment.mView)) { 1347 FragmentTransitionCompat21.excludeTarget(transition, fragment.mView, 1348 true); 1349 state.hiddenFragmentViews.add(fragment.mView); 1350 } 1351 } else { 1352 FragmentTransitionCompat21.excludeTarget(transition, fragment.mView, 1353 false); 1354 state.hiddenFragmentViews.remove(fragment.mView); 1355 } 1356 } 1357 } 1358 } 1359 } 1360 1361 private static void setNameOverride(ArrayMap<String, String> overrides, 1362 String source, String target) { 1363 if (source != null && target != null && !source.equals(target)) { 1364 for (int index = 0; index < overrides.size(); index++) { 1365 if (source.equals(overrides.valueAt(index))) { 1366 overrides.setValueAt(index, target); 1367 return; 1368 } 1369 } 1370 overrides.put(source, target); 1371 } 1372 } 1373 1374 private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames, 1375 ArrayList<String> targetNames) { 1376 if (sourceNames != null) { 1377 for (int i = 0; i < sourceNames.size(); i++) { 1378 String source = sourceNames.get(i); 1379 String target = targetNames.get(i); 1380 setNameOverride(state.nameOverrides, source, target); 1381 } 1382 } 1383 } 1384 1385 private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews, 1386 boolean isEnd) { 1387 int count = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size(); 1388 for (int i = 0; i < count; i++) { 1389 String source = mSharedElementSourceNames.get(i); 1390 String originalTarget = mSharedElementTargetNames.get(i); 1391 View view = namedViews.get(originalTarget); 1392 if (view != null) { 1393 String target = FragmentTransitionCompat21.getTransitionName(view); 1394 if (isEnd) { 1395 setNameOverride(state.nameOverrides, source, target); 1396 } else { 1397 setNameOverride(state.nameOverrides, target, source); 1398 } 1399 } 1400 } 1401 } 1402 1403 private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews, 1404 boolean isEnd) { 1405 int count = namedViews.size(); 1406 for (int i = 0; i < count; i++) { 1407 String source = namedViews.keyAt(i); 1408 String target = FragmentTransitionCompat21.getTransitionName(namedViews.valueAt(i)); 1409 if (isEnd) { 1410 setNameOverride(state.nameOverrides, source, target); 1411 } else { 1412 setNameOverride(state.nameOverrides, target, source); 1413 } 1414 } 1415 } 1416 1417 public class TransitionState { 1418 public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>(); 1419 public ArrayList<View> hiddenFragmentViews = new ArrayList<View>(); 1420 1421 public FragmentTransitionCompat21.EpicenterView enteringEpicenterView = 1422 new FragmentTransitionCompat21.EpicenterView(); 1423 public View nonExistentView; 1424 } 1425} 1426