Android Scene Transition Animation when starting multiple activities - android

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.

Related

One-Single Activity or Two Activities

My app's main Activity (containing a NavigationDrawer) allows to navigate through many (20 aprox) Fragments, because of navDrawer item clicks and other views' clicks inside each fragment.
Then, it moves to a point where I need a BottomNavigationView (maintaining also the navDrawer). From this point, because of the bottomNavView and other views' clicks, I can move to other different 10-15 fragments, aprox, and also to the ones that the main NavigationDrawer allows, but, in case I move to a Fragment through a click on any main navDrawer's item, the bottomNavView should be hidden.
So, is it correct here to use a One-Single Activity approach and be controlling the visibility of the bottomNavView or shall I use Two Activities in order to avoid being pendent of this in all navigations?
I don't believe there's a "right or wrong" answer in this case.
It really boils down to how you want to architect your application, as long as you're consistent.
If your fragments have a "state" and a ViewMOdel and such, then a single activity swapping fragments while controlling its own state (when to show the bottom bar) may be simpler than having to maintain two different activities, since navigation is done always between fragments.
It will also be tied to how the backstack behaves in each case (so test accordingly to ensure you get the expected behavior).
Simple Idea (with a single act)
This is pseudo-code, not perfect, compiling, functional code.
class BottomBarUseCase() {
operator fun invoke(destination: String): Boolean =
when (destination) {
"A", "B", "C" -> true
else -> false
}
}
Your Activity's ViewModel (greatly simplified of course)
class XXXViewModel(
private val bottomBarUseCase: BottomBarUseCase
): ViewModel() {
private val _state = MutableLiveData<YourState>(YourState.Empty)
fun setupBottomBar(destination: String) {
if (bottomBarUseCase(destination)) {
_state.value = SomeState.ShowBar
} else {
_state.value = SomeState.HideBar
}
}
Your Activity observes the state and does what it needs to do.
There are ways to streamline this and what not, but essentially, you're delegating the responsibility to show the bar to the use-Case, which you can test in isolation to ensure it does what you want it to do (aka: don't show the bar for certain destinations).
Your Fragments don't care about any of this (unless they too, need to make the decision, in which case you can still inject the useCase in the Fragment's viewModels and ask there too, since the useCase doesn't have any special dependency).
That's what I would do, but without having to do this in real life, it's hard to visualize whether this would have other drawbacks.
In general, this is how I would approach a problem that needs to be resolved elsewhere in many places: isolating it.
Hope that clarifies it.

ExitTransition doesn't work if not set up in onCreate

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

Android Jetpack Navigation Pass Lambda/Delegate between Fragments

I would like to pass a lambda from fragment A to fragment B when A transitions to B via a findNavController().navigate(R.id.action_a_to_b). The use case is B helps pick an item out to display on screen A.
Something like:
// In A
findNavController().navigate(R.id.action_a_to_b, configBlock: { fragmentB ->
fragmentB.itemSelectedCallback = this::itemSelected
})
I recognize this pattern doesn't quite fit with what Google is pushing (I assume they want shared observed view models with fragments not communicating between each other) but I am not looking to transition to that architecture style yet.
This is not yet possible, however, there is an existing feature request for being able to navigate for a result, which would let you get this type of functionality.

Best way to start one activity or another basing on a variable

I want to develop an application that, at the beginning, checks a variable's value and, basing on this value, starts the activity A or the activity B, something like this:
protectec void onCreate(...){
boolean b = checkVariable();
if(b){
startActivityA();
} else {
startActivityb();
}
}
What I'm doing
This is the method I have currently implemented:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_limitation);
varChecker = new VarChecker(this);
if(varChecker.getRemainingUses() <= 0){
limitReached();
} else if(varChecker.isFirstTime()){
firstTime();
} else {
startMainActivity();
}
}
This is an activity that must be shown the first time the application is executed. Else, depending on the getRemainingUses() result, it must start the activity A or the activity B automatically.
The question
Is there any way to do what I want, without the need of create a new Activity, specially to avoid the super.onCreate(savedInstanceState) and the setContentView(R.layout.activity_limitation)?
Is there any way to do what I want, without the need of creating a new
Activity, specially to avoid the super.onCreate(savedInstanceState)
and the setContentView(R.layout.activity_limitation)?
I had a similar problem some time ago. As far as I know, It's not possible to avoid calling super.onCreate(savedInstance) and setContentView(R.layout.activity_limitation). It's design of the Android activity.
I see the following possible solutions:
if you want to choose an appropriate activity by e.g. clicking a
button, then you just need to create a new Intent basing on a
variable and there's no problem.
if you want to choose an activity in
a different moment of the flow - e.g. during the application start you can:
create a single activity for two options, use fragments and switch between them
create "dummy" activity with an empty layout, create an intent and switch to an appropriate activity basing on a variable and finish "dummy" activity. Please note that this solution is a kind of workaround and it's worth to spend some time to figure out something better, but I'm not sure if there is a recommended solution for such use cases.

android: using ActivityGroup to embed activities

Im in the conceptualizing/design phase of building an app and i've hit a bit of a snag. Essentially i was looking for a way to embed one activity into the UI of another similar to how a TabHost/TabActivity. There would be a window at the top of the screen which would contain the other activity, and below that would be buttons and controls that are independent of the above activity and should always be visible. The user would be able to navigate from one activity to another in the window without causing any change to the below controls.
While looking into the issue i ran across ActivityGroup, which looked like it would be useful, but how would i implement it? Anyone have experience with ActivityGroup or have another idea?
Yes, you'd implement an ActivityGroup, which will be the container of your other Activities. When the user clicks one of the buttons, you'd get a reference to the LocalActivityManager, and use it to start, and embed the inner activity. Something like this:
LocalActivityManager mgr = getLocalActivityManager();
Intent i = new Intent(this, SomeActivity.class);
Window w = mgr.startActivity("unique_per_activity_string", i);
View wd = w != null ? w.getDecorView() : null;
if(wd != null) {
mSomeContainer.addView(wd);
}
Note, using this method can be pretty complicated, because unless the focus is just right, pressing the hardware buttons (like the menu button) will only only trigger events on the ActivityGroup instead of the inner Activity. You have to find some way to focus the inner activity after you add it to the container view, at which point the even will happen in the inner activity and propagate to the container activity.
It can be done, I've done it... and it works. It's just a bit more complicated than I think it should be.
Anyway, I got most of this information by looking at the TabHost code, which can be found here

Categories

Resources