I think what you want is to have a single activity and add a fragment for the 1st screen. Then when you want to navigate away (what you do now with startActivity) you should instead replace the fragment.
Something like this:
class MainActivity: Activity() {
onCreate(...) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
add1stFragment()
}
fun add1stFragment(){
supportFragmentManager
.beginTransaction()
.replace(R.id.fragmentContainer, LandingFragment())
.commitAllowingStateLoss()
}
}
class LandingFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// inflate layout with the contents of 1st screen
}
fun add2ndFragment(){
supportFragmentManager
.beginTransaction()
.replace(R.id.fragmentContainer, AlternativeFragment())
.commitAllowingStateLoss()
}
}
class AlternativeFragment : Fragment() {
fun onCreateView(...){...
}
}
res/layout/activity_main:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp">
<com.google.android.material.appbar.MaterialToolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="TITLE" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</FrameLayout>
</LinearLayout>
</layout>
Now the toolbar and background are the same for both screens, because they belong to the activity. While the screen contents belong to the different fragments.
The only downside is that if you rotate the screen the animation would start again. If you want that to continue from where it was you can use a couple things, but they will depend on how exactly you're doing the animation.
Unfortunately I tried giving a quick look at AnimationDrawable and I don't see how to make it jump to a specific point in its animation, nor how to get the current progress. Otherwise it would looks something like this (in case you used a different animation method):
class MainActivity: Activity {
private var animDrawable : AnimatedDrawable? =null
onCreate(savedInstanceState:Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
animDrawable = root_layout.background as? AnimationDrawable
fromIndex = getIndex(savedInstanceState)
add1stFragment()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("POSITION",animDrawable.getPosition)
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
startAnimation(fromIndex) // extract the current position and start playing from there
}
Make sure to check the background of your fragments is transparent. And optionally, set AppBarLayout opacity with android:alpha="0.5". It's questionable taste but it will allow the background to shine through.
Edit: so there is a very roundabout way to get the current position and resume from a given position, as shown in https://stackoverflow.com/a/13613613/2754856
Also, just figured out that you shouldn't call AnimationDrawable::start from onCreate: file:///home/fabiomint/bin/astudio/android-sdk/docs/reference/android/graphics/drawable/AnimationDrawable.html#start() and made some edits to match that.
Edit 2 for handling multiple activities:
It also works for screen rotation. It's not as good as using the same animation on the same activity, because the act of pausing/resuming animation on a different activity has a side effect of making a copy of the animation with the different frames with switched orders.
As I mentioned above, having a single activity is my 1st suggestion. The 2nd one is using another animation technique for this, AnimationDrawable is a pain to work with. ObjectAnimator is more friendly and it's possible to build gradient animation with it, but it can't animate shapes and other things that I believe only AnimationDrawable supports, like animated vectors.
make a singleton or global variable:
var activityBackground = PausableAnimationDrawable()
then on each of your activities do
override fun onResume() {
super.onResume()
findViewById<View>(R.id.root)
?.let {
it.background = activityBackground.getAnimation(this)
}
activityBackground.resume()
}
override fun onPause() {
super.onPause()
activityBackground.pause()
}
class PausableAnimationDrawable {
private var activeAnimation: AnimationDrawable? = null
private var animation: AnimationDrawable? = null
private var indexFrame: Int = 0
fun pause() {
indexFrame = getCurrentIndex()
}
fun resume() {
activeAnimation?.start()
activeAnimation?.setVisible(true, false)
}
fun getAnimation(context: Context): AnimationDrawable? {
if (animation == null) {
animation = getNewAnimation(context)
}
activeAnimation = getReindexedAnimation(animation, indexFrame)
return activeAnimation
}
private fun getReindexedAnimation(
animation: AnimationDrawable?,
indexFrame: Int
): AnimationDrawable? {
val animation = animation ?: return null
return AnimationDrawable()
.also { newDrawable ->
for (i in indexFrame..animation.numberOfFrames) {
animation.getFrame(i)
?.let {
newDrawable.addFrame(it, animation.getDuration(i))
}
}
for (i in 0 until indexFrame) {
animation.getFrame(i)
?.let {
newDrawable.addFrame(it, animation.getDuration(i))
}
}
}
}
private fun getNewAnimation(context: Context): AnimationDrawable? {
return context.applicationContext.getDrawable(R.drawable.gradient)
?.let { it as? AnimationDrawable }
?.also { it.setVisible(false, false) }
}
private fun getCurrentIndex(): Int {
for (i in 0..(activeAnimation?.numberOfFrames ?: 0)) {
if (activeAnimation?.getFrame(i) == activeAnimation?.current) {
Timber.d("*** Frame %s match", i)
return (i + indexFrame) % (activeAnimation?.numberOfFrames ?: 0)
} else {
Timber.d("*** Frame %s no match", i)
}
}
return 0
}
}