Status Update
Comments
pe...@airbnb.com <pe...@airbnb.com> #2
Jeremy, is this still an issue? I think the problem was that you had two transitions targeting the same View for the same action (e.g. two Slide() transitions).
il...@google.com <il...@google.com>
ap...@google.com <ap...@google.com> #3
I have a similar issue with plain AnimatorSet:
set.start()
set.pause()
set.setCurrentPlayTime(100)
set.setCurrentPlayTime(0)
set.setCurrentPlayTime(100)
set.resume()
doesn't play animation in resume().
jb...@google.com <jb...@google.com> #4
Should clarify that if I filter out setCurrentPlayTime(0)
(or replace it with setCurrentPlayTime(1)
) it works well.
Also even with setCurrentPlayTime(0)
, onAnimationEnd
is notified with correct delay (as if the animation has played).
an...@google.com <an...@google.com> #5
@
I think that is intended for Animator. If you set the currentPlayTime
to 0 or the total duration the animator completes. We do some
da...@gmail.com <da...@gmail.com> #6
Did some investigation on the Fragment side and it seems like the merged transition is targeting correctly.
Exiting Transition: Slide@aa9288e: tgts(android.widget.LinearLayout{f9add3d})
>>>>> ExitingViews <<<<<
View: android.widget.LinearLayout{f9add3d}
Entering Transition: Slide@35b8af: tgts(android.widget.LinearLayout{b7f24bc})
>>>>> EnteringViews <<<<<
View: android.widget.LinearLayout{b7f24bc}
Final merged transition: TransitionSet@7bc1c45:
TransitionSet@e133f9a:
Slide@aa9288e: tgts(android.widget.LinearLayout{f9add3d})
Slide@35b8af: tgts(android.widget.LinearLayout{b7f24bc})
merged transition passed to controlDelayedTransition: TransitionSet@7bc1c45:
TransitionSet@e133f9a:
Slide@aa9288e: tgts(android.widget.LinearLayout{f9add3d})
Slide@35b8af: tgts(android.widget.LinearLayout{b7f24bc})
Still digging.
il...@google.com <il...@google.com> #7
Branch: androidx-main
commit 567b7459329d1ec8d27a8c6fe1c4a86442065d7d
Author: Jeremy Woods <jbwoods@google.com>
Date: Tue Sep 26 20:06:54 2023
Add additional logging for transitions
Adding more debug logging in transitions to track the entering and
exiting transitions as well as the final merged transition and its
targets.
Test: added logging
Bug: 300157785
Change-Id: I0d9ad72b865422493c6c895ddb6115abf85eed16
M fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
ya...@gmail.com <ya...@gmail.com> #8
So I have isolated this outside of fragment into something much simpler and I think it breaks down when it comes to the adding and removing of Views with animateToStart.
The attached sample is a simple add that goes between two screens BLUE
and GREEN
. It has code for both the 1.5.0-alpha03
and 1.5.0-alpha04
versions, but I think alpha04 is currently broken in another way so I will upload the alpha03 version here.
This is integrated with predictive back similar to how fragment is, so upon cancelling we call animateToStart
, then we do a beginDelayedTransition
on a 0
duration Fade()
and we reverse the view visibility back to what it was prior to starting the transition.
If you only do visibility, cancel always works the view never goes away, it is wonderful, but when you do adding and removing views like we need to in fragment it fails.
First the code for beginDelayedTransition goes from this:
TransitionManager.beginDelayedTransition(container, Fade().apply {
duration = 0
})
reverseViews()
to this:
TransitionManager.beginDelayedTransition(container, Fade().apply {
duration = 0
addListener(onEnd = {
reverseViews()
blueScreen.visibility = View.VISIBLE
greenScreen.visibility = View.VISIBLE
})
})
reverseViews(useVisibility = true)
We need to make this change because after the animateToStart()
view is still parented by the overlay, so we call reverseViews(useVisibility = true)
to only change the visibility and then once the transition finishes we can call reverseViews()
to parent the view properly, then we make both views visible again.
From our perspective after the 0
duration transition our views are back in the proper state, but they do not transition properly after a cancel.
If the app is doing this wrong and we can make the appropriate fixes, doing the same in fragment should resolve this. There is logging available that shows the state of the views when we start the transition.
il...@google.com <il...@google.com> #9
The API has changed since that project was created in a way that makes the API more robust. I'm hoping that has fixed this...
Description
Version used: 1.1.0-rc01 (part of AppCompat 1.1.0-rc01)
Devices/Android versions reproduced on: API 28 x86 emulator and Pixel 1
When using the new `BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT` behavior on a `FragmentStatePagerAdapter`, along with `getItemPosition()` returning `POSITION_NONE`, the app crashes when calling `notifyDataSetChanged()` on the adapter. We are using this paradigm to replace all of the fragments in the adapter.
Here's what seems to be happening. All of the fragments are removed in a fragment transaction (`ViewPager` calls `FragmentStatePagerAdapter#finishUpdate()` once the fragments are removed). Then the new fragments are instantiated and added to a new fragment transaction. The `ViewPager` then calls `setPrimaryItem` which tries to set the max lifecycle on the `mCurrentPrimaryItem`. However, this `mCurrentPrimaryItem` is still referencing the old fragment that was removed in a separate transaction, which causes the crash. It seems like the `mCurrentPrimaryItem` should be nulled out if it is removed from the adapter.
I've attached a sample project and GIF. To reproduce, just launch the app and press the `Replace Fragments` action button. Here's the error stacktrace:
2019-07-10 20:03:16.876 32176-32176/com.example.android.horizontalpaging E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.horizontalpaging, PID: 32176
java.lang.IllegalArgumentException: Cannot setMaxLifecycle for Fragment not attached to FragmentManager FragmentManager{9187988 in HostCallbacks{ba35f21}}
at androidx.fragment.app.BackStackRecord.setMaxLifecycle(BackStackRecord.java:244)
at androidx.fragment.app.FragmentStatePagerAdapter.setPrimaryItem(FragmentStatePagerAdapter.java:234)
at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1234)
at androidx.viewpager.widget.ViewPager.setCurrentItemInternal(ViewPager.java:669)
at androidx.viewpager.widget.ViewPager.setCurrentItemInternal(ViewPager.java:631)
at androidx.viewpager.widget.ViewPager.dataSetChanged(ViewPager.java:1086)
at androidx.viewpager.widget.ViewPager$PagerObserver.onChanged(ViewPager.java:3097)
at androidx.viewpager.widget.PagerAdapter.notifyDataSetChanged(PagerAdapter.java:291)
at com.example.android.horizontalpaging.MainActivity.onOptionsItemSelected(MainActivity.java:67)
at android.app.Activity.onMenuItemSelected(Activity.java:3544)
at androidx.fragment.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:384)
at com.android.internal.policy.PhoneWindow.onMenuItemSelected(PhoneWindow.java:1237)
at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:776)
at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:148)
at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:923)
at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:913)
at android.widget.ActionMenuView.invokeItem(ActionMenuView.java:617)
at com.android.internal.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:147)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)