Android 共享元素转场效果
Android共享元素转场效果实现界面间平滑过渡动画。通过为UI元素添加android:transitionName属性,使用ActivityOptionsCompat.makeSceneTransitionAnimation()配置共享元素,可在Activity间创建连续视觉体验。源Activity设置共享元素并传递数据,目标Activity接收数据并保持相同transitionName。对于多
·
Android 共享元素转场效果
概述
Android 共享元素转场(Shared Element Transition)是一种在 Activity 或 Fragment 之间进行平滑过渡的动画效果。它允许指定 UI 元素在两个界面间"共享",从而创建连续的视觉体验,让用户感觉元素是从一个界面"移动"到了另一个界面。
Activity之间的共享元素转场
简单使用
源Activity:
- 在布局文件中,为共享元素添加
android:transitionName属性 - ActivityOptionsCompat.makeSceneTransitionAnimation():配置共享元素
class OneActivity : AppCompatActivity() {
private val image = R.drawable.cherry
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_one)
imageView.setImageResource(drawableRes)
imageView.setOnClickListener {
val intent = Intent(context, TwoActivity::class.java).apply {
putExtra("image", image)
}
// 共享元素配置
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this@OneActivity,
imageView,
"image_transition"
)
startActivity(intent, options.toBundle())
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".OneActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:transitionName="image_transition" />
</LinearLayout>
目标Activity:
- 在布局文件中,为对应元素添加
android:transitionName属性 - postponeEnterTransition():暂停转场,等待资源加载完成
- startPostponedEnterTransition():开始转场
class TwoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_two)
val image = intent.getIntExtra("image", -1)
imageView.setImageResource(image)
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".TwoActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:transitionName="image_transition" />
</LinearLayout>
多个共享元素处理
源Activity:
class SrcActivity : AppCompatActivity() {
private lateinit var context: Context
private lateinit var imageView: ImageView
private lateinit var textView: TextView
private val image = R.drawable.cherry
private val text = "hello world"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_src)
context = this
imageView = findViewById(R.id.imageView)
textView = findViewById(R.id.textView)
imageView.setImageResource(image)
textView.text = text
imageView.setOnClickListener {
val intent = Intent(context, DestActivity::class.java).apply {
putExtra("image", image)
putExtra("text", text)
}
val options = ActivityOptions.makeSceneTransitionAnimation(
this@SrcActivity,
android.util.Pair.create(imageView, "image_transition"),
android.util.Pair.create(textView, "text_transition"),
)
startActivity(intent, options.toBundle())
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".SrcActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:transitionName="image_transition" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:transitionName="text_transition" />
</LinearLayout>
目标Activity:
class DestActivity : AppCompatActivity() {
private lateinit var imageView: ImageView
private lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dest)
imageView = findViewById(R.id.imageView)
textView = findViewById(R.id.textView)
val image = intent.getIntExtra("image", -1)
val text = intent.getStringExtra("text") ?: ""
imageView.setImageResource(image)
textView.text = text
}
}
<?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"
android:orientation="vertical"
tools:context=".DestActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:transitionName="image_transition" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:scaleType="centerCrop"
android:textSize="24sp"
android:transitionName="text_transition" />
</FrameLayout>
Fragment之间的共享元素转场
源Fragment:
class SrcFragment : Fragment() {
private lateinit var imageView: ImageView
private lateinit var textView: TextView
private val image = R.drawable.cherry
private val text = "hello world"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_src, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imageView = view.findViewById(R.id.imageView)
textView = view.findViewById(R.id.textView)
imageView.setImageResource(image)
textView.text = text
imageView.setOnClickListener {
parentFragmentManager.beginTransaction()
.setReorderingAllowed(true) // 启用转场重排序
.addSharedElement(imageView, "image_transition")
.addSharedElement(textView, "text_transition")
.replace(R.id.fragment_container, DestFragment.newInstance(image, text))
.addToBackStack(null)
.commit()
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".SrcFragment">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:transitionName="image_transition" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:transitionName="text_transition" />
</LinearLayout>
目标Fragment:
class DestFragment : Fragment() {
private lateinit var imageView: ImageView
private lateinit var textView: TextView
private var image = -1
private var text = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
image = it.getInt("image", -1)
text = it.getString("text", "")
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_dest, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imageView = view.findViewById(R.id.imageView)
textView = view.findViewById(R.id.textView)
// 配置共享元素进入转场
sharedElementEnterTransition = TransitionInflater.from(requireContext())
.inflateTransition(android.R.transition.move)
// 配置共享元素返回转场
sharedElementReturnTransition = TransitionInflater.from(requireContext())
.inflateTransition(android.R.transition.move)
// 配置普通元素的进入和退出转场
enterTransition = Fade()
exitTransition = Fade()
imageView.setImageResource(image)
textView.text = text
}
companion object {
fun newInstance(image: Int, text: String) = DestFragment().apply {
arguments = Bundle().apply {
putInt("image", image)
putString("text", text)
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".DestFragment">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:transitionName="image_transition" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:scaleType="centerCrop"
android:textSize="24sp"
android:transitionName="text_transition" />
</FrameLayout>
与RecyclerView配合使用
数据模型:
class Item(val image: Int, val text: String)
Adapter:
class ListAdapter(
private val list: List<Item>,
private val onItemClick: (Item, View, View) -> Unit
) : RecyclerView.Adapter<ListAdapter.ViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false))
}
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
holder.imageView.transitionName = "image_transition"
holder.textView.transitionName = "text_transition"
list[position].let { item ->
holder.imageView.setImageResource(item.image)
holder.textView.text = item.text
holder.itemView.setOnClickListener {
onItemClick(item, holder.imageView, holder.textView)
}
}
}
override fun getItemCount() = list.size
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView = itemView.findViewById<ImageView>(R.id.imageView)
val textView = itemView.findViewById<TextView>(R.id.textView)
}
}
使用:
class ListActivity : AppCompatActivity() {
private lateinit var rv: RecyclerView
private val list = mutableListOf<Item>().apply {
for (i in 1..10) {
add(Item(R.drawable.apple, "苹果"))
add(Item(R.drawable.cherry, "樱桃"))
add(Item(R.drawable.pear, "梨子"))
add(Item(R.drawable.mango, "芒果"))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list)
rv = findViewById(R.id.rv)
val adapter = ListAdapter(list) { item, imageView, textView ->
if (imageView.isAttachedToWindow && textView.isAttachedToWindow) {
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
Pair.create(imageView, "image_transition"),
Pair.create(textView, "text_transition")
)
val intent = Intent(this, DestActivity::class.java).apply {
putExtra("image", item.image)
putExtra("text", item.text)
}
startActivity(intent, options.toBundle())
}
}
rv.adapter = adapter
}
}
更多推荐


所有评论(0)