ExitTransition doesn't work if not set up in onCreate - android

I would like to set up my exit transition with specific target views (addTarget) which I only know after the user has clicked an item, therefore I instantiate it only before starting the new activity.
However, this way the exit transition is not applied at all, event without the addTarget calls. When I navigate back and start the activity with transition again, it's working well.
private fun onItemClick(id: Long) {
window.exitTransition = Slide(Gravity.LEFT)
val activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(this, ..).toBundle()
val intent = Intent(this, SettingsActivity::class.java)
ActivityCompat.startActivity(this, intent, activityOptions)
}
Can I make the transition work also for the first time?

I think it is the correct behavior but tough to know without a code example.
The recommended way to change the transitions is
// inside your activity (if you did not enable transitions in your theme)
with(window) {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
// set an exit transition
exitTransition = Explode()
}
as discussed here: https://developer.android.com/training/transitions/start-activity
If you are having this problem in the AndroidStudio emulator specifically, you might want to update AndroidStudio. I had a problem in an older version where a button's behavior was always different the fist time I refreshed the app.

The exit transition must be set before calling ActivityOptions.makeSceneTransitionAnimation to make it work.
I figured it out by looking at these classes:
ActivityTransitionCoordinator
ExitTransitionCoordinator

Related

Android navigation component display multiple DialogFragments in a row

Hi folks I need to throw up a series of DialogFragments one after another using the navigation component. I have encountered some pretty unusual system behavior which looks like a race condition. I show the dialogs with a global action after an item is clicked, and use the fragment result api to determine if another one should be shown.
I am using a custom layout so there's no positive / negative listeners etc., and my own continue / cancel buttons send a result back to the host fragment.
ItemsFragment.kt:
navController.navigate(RegisterItemsDirections.openModsDialog(item.id, 0))
fragment.setFragmentResultListener(ItemsFragment.MODIFIERS_REQUEST) { key, bundle ->
//kill the current dialog
navController.navigateUp()
//some logic to determine if we need another dialog...
if(needAnotherDialog){
//navigate to the next one
navController.navigate(RegisterItemsDirections.openModsDialog(item.id, lastModGroupSelectionIndex + 1))
}
}
ModsDialogFragment.kt, when the user clicks "continue"
setFragmentResult(MODIFIERS_REQUEST, bundleOf(MODIFIERS_RESULT to selectedMods))
So the issue only appears on 3rd or more dialogs on my devices, I can see that the 1st and 2nd dialogs are completely destroyed and detached. When it displays the 3rd one, the first one attaches itself again, and appears beneath the 3rd one which I can't explain.
I've tried:
Popping the back stack in the global action's nav xml
Navigating up or dismissing the dialog fragment in the dialog itself (before calling setFragmentResult), which is the most logical place to put it
Popping the backstack instead of navigating up, basically the same thing in this case
When I don't dismiss / nav up any of the dialogs and allow them all to just stack, there's no issue. When I delay the navigation by 500ms there is no issue. So navving up and then immediately navigating to another instance of the dialog are fighting with eachother producing very strange results. What could be the solution here that doesn't involve a random delay?
Navigation version is 2.3.3 and I'm having a lot of trouble trying to update it due to AGP upgrades being incompatible with a jar I need so I'm not sure if this has been fixed.
I figured out the issue, it's down to dumb copy pasting.
I took the donut tracker sample code and made it stack dialogs in the same way and there was no issue.
The difference between the two was I am subclassing DialogFragment and for some unknown reason doing this:
override fun show(manager: FragmentManager, tag: String?) {
val transaction = manager.beginTransaction()
val prevFragment = manager.findFragmentByTag(tag)
if (prevFragment != null) {
transaction.remove(prevFragment)
}
transaction.addToBackStack(null)
show(transaction, tag)
}
This code predates the Nav component library I believe, and it completely f***s with the fragment manager used by the navigation component. So the moral of the story is to not do bizarre things in super classes, or better yet not super class at all.

How can I use button in one activity to do something in second activity?

I got a question. I have two activities in my app. In first one, when I click the button, I need something to happen in the second one. How can I do it? If that button would be in the second activity I would just do it by:
button.setOnClickListener {}
But how can I do it when button is in the other activity? It's worth adding that code, that tells what should happen, must be in that second activity, just like it was in that "setOnClickListener". Sorry, I'm starting with Android development.
You could communicate between two activities via broadcast or intent.
But it make logic more complex.
So I suggest use two fragments instead two activities.
If you use two fragment in one activity, you can easy communicate between two fragments.
You can look more detailed information about fragment from this URL.
https://developer.android.com/guide/fragments
To achieve the intended flow you may try the below approach,
Start activity 2 on button click from activity 1.
On activity 2 place your code in onCreate so, once activity 2 loads up your code will fire up.
Activity 1:
button.setOnClickListener{
val intent = Intent(this, second::class.java)
startActivity(intent)
}
Activity 2:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
CallDefinedFunctionHere();
}
You cannot be sure that both activities are present at the same moment since the system might destroy inactive one therefore you cannot trigger any code from activity A inside activity B.
What you can do you can start activity B with an intent and some parameters describing what should happen inside activity B.
or
You can communicate by writing something down to a persistent storage (like SharedPreferences) and then when the other activity is resumed (active again) reading it, reacting to it and then removing it from the storage (to make sure you do not handle it twice).
You can pass data in the intent that opens the second activity.
// In first activity:
buttonX.setOnClickListner {
val intent = Intent(MySecondActivity::class.java).apply {
putExtra("wasFromButtonX", true)
}
startActivity(intent)
}
// In second activity:
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val wasLaunchedFromButtonX = intent.getBooleanExtra("wasFromButtonX", false)
// Above line uses false as default, so it will only be true if you explicitly
// put the extra in the Intent that started this Activity.
if (wasLaunchedFromButtonX) {
// do alternate setup here
}
}
How to pass information between Activities is explained in the introductory documentation here.
Create a function in class where 2nd activity are defined like this.
public void refresh(){}
Now Call that in your 1st activity where you want to call 1st after any action.
button.setOnClickListener {((MainActivity) Objects.requireNonNull(getActivity())).refresh();}

How can i go to previous fragment when i am using navigation

i am now currently in signupfragment and need to go to loginfragment which is my previous fragment but its not working
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
Log.e("BackPressed","Onbackpressed")
findNavController().popBackStack(R.id.signupfragment,false)
}
The second parameter to popBackStack(R.id.signupfragment,false) is popUpToInclusive:
popUpToInclusive: boolean: Whether the given destination should also be popped.
If you use popUpToInclusive of false, you are saying "pop everything up to R.id.signupfragment, but not R.id.signupfragment.
Therefore, if you are at R.id.signupfragment, this correctly pops nothing (and returns false). You want to either 1) use true for popUpToInclusive or 2) use the version of popBackStack() that doesn't take any parameters, as this is the equivalent of popping just your current destination off the back stack:
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
Log.e("BackPressed","Onbackpressed")
findNavController().popBackStack()
}
Of course, Navigation already sets up the back button to call popBackStack() when you hit the back button, so besides your extra logging, your call is entirely unnecessary.
Another way of doing it easily is by putting a test back button and coding it with.
testBackButton.setOnClickListener {
val intent = Intent(this, ActivityThatYouWantToGo::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK.or(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
I dont know its a good answer but its working
findNavController().popBackStack()
findNavController().popBackStack()
findNavController().navigate(R.id.loginFragment)

Android Scene Transition Animation when starting multiple activities

I'd like to use ActivityOptionsCompat.makeSceneTransitionAnimation to do a scene transition between 2 UI elements but I also need to start multiple activities. This code causes issues (prevents the second activity from even starting). I can start the activities individually to pass the options to the correct activity, but that has its own issues with a flickering UI. Is there another way to accomplish this?
if (targetActivity == TargetActivity.HOMEPAGE) {
baseActivity.startActivity(homeIntent)
} else {
val targetIntent = argumentBuilder.toIntent(baseActivity, targetActivity.clazz)
val intents = arrayOf(homeIntent, targetIntent)
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(baseActivity, view.targetUserImage, "transition_to_inbox")
baseActivity.startActivities(intents, options.toBundle())
}
That isn't supported. The framework is designed to transition from one Activity to another within the same task. If you start multiple activities, it has no idea what you intend with respect to UI elements.

How to disable supportFinishAfterTransition when backing out from an activity

I have an activity I animate to with a transition animation, like this:
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, view, transitionStr);
ActivityCompat.startActivity(activity, intent, options.toBundle());
However, when I go back, I don't want the animation to run in inverse. Is that possible? I'm using AppCompatActivity from appcompat-v7:23.1.1.
If you never want to have that transition back to the parent activity, use
finish();
You can wrap it around a condition if you sometimes want to the transition on the way back to the parent activity. An example use case would be to disable the transition when an interstitial ad was displayed:
if (interstitialAdWasDisplayed) {
finish();
} else {
finishAfterTransition();
}
A possible duplicate of Overriding Transition
finish();
Details.this.overridePendingTransition(R.anim.nothing,R.anim.nothing);
With this piece of code, you can override the finish animation of the current activity.
According to the answers to this question you can't run an animation in inverse. Instead I would create another transition which is basically the inverse of the transition you use when you start the activity and add it to the Activity like this (R.anim.inverseTransition is the Transition you created):
finish();
Details.this.overridePendingTransition(R.anim.inverseTransition,R.anim.inverseTransition);
(see this answer for more information)

Categories

Resources