Skip to content

Commit

Permalink
Playing around with shared element transitions for Fragments
Browse files Browse the repository at this point in the history
  • Loading branch information
isaac-udy committed Sep 3, 2024
1 parent d6a034f commit f3f12bd
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package dev.enro.destination.fragment

import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.findFragment
import dev.enro.core.R
import java.util.WeakHashMap

internal class SharedElementContainer(
val map: WeakHashMap<View, String> = WeakHashMap()
)

internal val Fragment.sharedElementContainer: SharedElementContainer?
get() = view?.getTag(R.id.enro_internal_shared_element_container_id) as? SharedElementContainer

public fun View.addSharedElement(name: String) {
val rootFragmentView = runCatching { findFragment<Fragment>() }
.getOrNull()
?.view ?: return

val sharedElementContainer = rootFragmentView.getTag(R.id.enro_internal_shared_element_container_id) as? SharedElementContainer
?: SharedElementContainer().apply { rootFragmentView.setTag(R.id.enro_internal_shared_element_container_id, this) }

sharedElementContainer.map[this] = name
}

public fun View.clearSharedElement() {
val rootFragmentView = runCatching { findFragment<Fragment>() }
.getOrNull()
?.view ?: return

val sharedElementContainer = rootFragmentView.getTag(R.id.enro_internal_shared_element_container_id) as? SharedElementContainer
?: SharedElementContainer().apply { rootFragmentView.setTag(R.id.enro_internal_shared_element_container_id, this) }

sharedElementContainer.map.remove(this)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import dev.enro.core.controller.get
import dev.enro.core.controller.interceptor.builder.NavigationInterceptorBuilder
import dev.enro.core.controller.usecase.HostInstructionAs
import dev.enro.core.navigationContext
import dev.enro.destination.fragment.sharedElementContainer
import dev.enro.extensions.animate
import dev.enro.extensions.getParcelableCompat

Expand Down Expand Up @@ -172,6 +173,9 @@ public class FragmentNavigationContainer internal constructor(
)
toRemoveDirect.forEach {
remove(it)
it.sharedElementContainer?.map?.forEach {
addSharedElement(it.key, it.value)
}
ownedFragments.remove(it.tag)
}
runOnCommit {
Expand All @@ -181,6 +185,9 @@ public class FragmentNavigationContainer internal constructor(
}
}
toDetach.forEach {
it.fragment.sharedElementContainer?.map?.forEach {
addSharedElement(it.key, it.value)
}
detach(it.fragment)
}
if (activePushed != null) {
Expand Down
1 change: 1 addition & 0 deletions enro-core/src/main/res/values/id.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
<item type="id" name="enro_internal_single_fragment_frame_layout" />
<item type="id" name="enro_internal_compose_dialog_fragment_view_id" />
<item type="id" name="enro_internal_compose_fragment_view_id" />
<item type="id" name="enro_internal_shared_element_container_id" />
</resources>
1 change: 1 addition & 0 deletions tests/application/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<activity android:name="dev.enro.tests.application.compose.CloseRootAndPresentActivity" />
<activity android:name="dev.enro.tests.application.activity.SimpleActivityImpl" />
<activity android:name="dev.enro.tests.application.compose.results.ComposeEmbeddedResultFlowActivity" />
<activity android:name="dev.enro.tests.application.fragment.FragmentSharedElementActivity" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package dev.enro.tests.application.fragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import dev.enro.annotations.NavigationDestination
import dev.enro.core.NavigationKey
import dev.enro.core.compose.navigationHandle
import dev.enro.core.fragment.container.navigationContainer
import dev.enro.core.navigationHandle
import dev.enro.core.push
import dev.enro.core.requestClose
import dev.enro.destination.fragment.addSharedElement
import dev.enro.test.application.R
import kotlinx.parcelize.Parcelize

@Parcelize
object FragmentSharedElementDestination : NavigationKey.SupportsPresent {
@Parcelize
internal class FirstFragmentDestination : NavigationKey.SupportsPush
@Parcelize
internal class SecondFragmentDestination : NavigationKey.SupportsPush
}

@NavigationDestination(FragmentSharedElementDestination::class)
class FragmentSharedElementActivity : AppCompatActivity() {
val container by navigationContainer(
containerId = R.id.fragment_container,
root = { FragmentSharedElementDestination.FirstFragmentDestination() }
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_shared_element_activity)
}
}

@NavigationDestination(FragmentSharedElementDestination.FirstFragmentDestination::class)
class FragmentSharedElementFirstFragment : Fragment() {

private val navigation by navigationHandle<NavigationKey>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = TransitionInflater.from(requireContext())
.inflateTransition(android.R.transition.move)
sharedElementReturnTransition = TransitionInflater.from(requireContext())
.inflateTransition(android.R.transition.move)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_shared_element_first, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<ImageView>(R.id.shared_image)
.addSharedElement("fragment_image")

view.findViewById<Button>(R.id.next_button)
.setOnClickListener { navigation.push(FragmentSharedElementDestination.SecondFragmentDestination()) }
}
}

@NavigationDestination(FragmentSharedElementDestination.SecondFragmentDestination::class)
@Composable
fun FragmentSecond() {
val navigation = navigationHandle()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize(),
) {
Text("compose")
Spacer(Modifier.height(128.dp))
AndroidView(
modifier = Modifier.size(256.dp),
factory = { context ->
ImageView(context).apply {
setImageResource(R.drawable.ic_launcher_foreground)
id = R.id.shared_image
setColorFilter(0xFFFF00FF.toInt())
transitionName = "fragment_image"
}
},
update = {
it.addSharedElement("fragment_image")
}
)
Spacer(Modifier.height(128.dp))
Button(
onClick = { navigation.requestClose() }
) {
Text("Close")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

</FrameLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
xmlns:app="http://schemas.android.com/apk/res-auto">

<View
android:layout_width="1dp"
android:layout_height="128dp"/>

<ImageView
android:id="@+id/shared_image"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_launcher_foreground"
app:tint="#FF00FF"
android:transitionName="fragment_image"
/>

<View
android:layout_width="1dp"
android:layout_height="128dp"/>

<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="Next"/>


</LinearLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
xmlns:app="http://schemas.android.com/apk/res-auto">

<View
android:layout_width="1dp"
android:layout_height="128dp"/>

<ImageView
android:id="@+id/shared_image"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_launcher_foreground"
app:tint="#FF00FF"
android:transitionName="fragment_image"
/>

<View
android:layout_width="1dp"
android:layout_height="128dp"/>

<Button
android:id="@+id/back_button"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="Back"/>


</LinearLayout>

0 comments on commit f3f12bd

Please sign in to comment.