从网格 RecyclerView 到细节的共享过渡效果 activity
Shared transition effect from grid RecyclerView to details activity
我正在尝试从具有网格布局的 RecyclerView
到细节 activity 之间的第一个共享过渡。
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_cryptocurrencies"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never" />
首先,transitionName
在详细信息布局和 RecyclerView
的布局中都设置为 "cryptocurrency_name"。
<!-- RecyclerView's item layout -->
<TextView
style="@style/CurrencyTextView"
android:id="@+id/tv_cryptocurrency_name"
android:textSize="20sp"
android:maxLength="12"
android:transitionName="cryptocurrency_name"
tools:text="Bitcoin" />
...
<!-- Details -->
<TextView
android:id="@+id/tv_cryptocurrency_detail_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bitcoin"
android:textSize="32sp"
android:transitionName="cryptocurrency_name"
.../>
MainActivity
来自适配器的覆盖方法:
private var tvCryptocurrencyName: TextView? = null
tvCryptocurrencyName = rv_cryptocurrencies.findViewById(R.id.tv_cryptocurrency_name)
override fun onItemClick(cryptocurrency: Cryptocurrency) {
val intent = Intent(this, DetailActivity::class.java)
val options = ActivityOptions.makeSceneTransitionAnimation(this, tvCryptocurrencyName, "cryptocurrency_name")
intent.putExtra(EXTRA_STRING_CRYPTOCURRENCY_DETAILS, cryptocurrency)
startActivity(intent, options.toBundle())
}
在adapter中设置了一个接口,当按下一个item时触发的方法:
interface OnItemClickListener {
fun onItemClick(cryptocurrency: Cryptocurrency)
}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
val cryptocurrency = cryptocurrenciesList[position]
holder.populateUi(cryptocurrency)
holder.itemView.setOnClickListener { onItemClickListener?.onItemClick(cryptocurrenciesList[position]) }
}
当我按查看结果时,应用崩溃并显示消息
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.gabriel.cryptocurrencies, PID: 4416
java.lang.IllegalArgumentException: Shared element must not be null
at android.app.ActivityOptions.makeSceneTransitionAnimation(ActivityOptions.java:694)
at android.app.ActivityOptions.makeSceneTransitionAnimation(ActivityOptions.java:649)
at com.example.gabriel.cryptocurrencies.ui.main.MainActivity.onItemClick(MainActivity.kt:50)
at com.example.gabriel.cryptocurrencies.ui.main.MainAdapter$onBindViewHolder.onClick(MainAdapter.kt:36)
at android.view.View.performClick(View.java:6261)
at android.view.View$PerformClick.run(View.java:23752)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
和其他教程一样,这就是我需要做的。也许我尝试从 RecyclerView
的布局访问字段的方式有误?
编辑
更新但失败的代码。现在,动画似乎会冻结,因为当我按下后退按钮一次时,它不会离开应用程序,只有按下两次。此外,在编辑代码和部署更改时,应用会在尝试切换到详细信息视图后加载详细信息视图。
MainActivity
:
override fun onItemClick(cryptocurrency: Cryptocurrency, field: View) {
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra(EXTRA_CRYPTOCURRENCY_DETAILS, cryptocurrency)
intent.putExtra(EXTRA_TRANSITION_NAME, cryptocurrency.id)
val options = ActivityOptions.makeSceneTransitionAnimation(this, field, cryptocurrency.id)
startActivity(intent, options.toBundle())
}
MainAdapter
:
val tvCryptocurrencyName = holder.itemView.tv_cryptocurrency_name
interface OnItemClickListener {
fun onItemClick(cryptocurrency: Cryptocurrency, field: View)
}
holder.itemView.setOnClickListener { onItemClickListener?.onItemClick(cryptocurrenciesList[position], tvCryptocurrencyName) }
您必须使用 onBindViewHolder
方法为 RecyclerView
适配器中的每个项目设置唯一的转换名称。
像这样:
ViewCompat.setTransitionName(holder.ivIcon, cryptocurrency.id.toString())
通过 Intent
发送 transitionName
,在您的详细信息 activity 中获取此 transitionName
,然后将其设置为您的 View
:
val iconTransitionName = intent.getStringExtra(EXTRA_TRANSITION_NAME)
ivIcon.transitionName = iconTransitionName
更新:
在Adapter
的onBindViewHolder
方法中:
ViewCompat.setTransitionName(holder.itemView, cryptocurrenciesList[position].id)
在onItemClick()
中:
override fun onItemClick(cryptocurrency: Cryptocurrency, textView: TextView) {
val transitionName = ViewCompat.getTransitionName(textView)
val intent = Intent(this, DetailActivity::class.java)
val options = ActivityOptions.makeSceneTransitionAnimation(this, textView, transitionName) // You should send exactly this instace of TextView, not by it's id
intent.putExtra(EXTRA_STRING_CRYPTOCURRENCY_DETAILS, cryptocurrency)
intent.putExtra(EXTRA_STRING_TRANSITION_NAME, cryptocurrency.id.toString())
startActivity(intent, options.toBundle())
}
在DetailActivity
的onCreate()
中:
val transitionName = intent.getStringExtra(EXTRA_STRING_TRANSITION_NAME)
tv_cryptocurrency_detail_name.transitionName = transitionName
在values-v21/styles.xml
中添加:
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="android:windowContentTransitions">true</item>
</style>
并将此添加到您的 TextView
s:
style="@style/MaterialAnimations"
查看简单示例:Github Repo
我正在尝试从具有网格布局的 RecyclerView
到细节 activity 之间的第一个共享过渡。
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_cryptocurrencies"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never" />
首先,transitionName
在详细信息布局和 RecyclerView
的布局中都设置为 "cryptocurrency_name"。
<!-- RecyclerView's item layout -->
<TextView
style="@style/CurrencyTextView"
android:id="@+id/tv_cryptocurrency_name"
android:textSize="20sp"
android:maxLength="12"
android:transitionName="cryptocurrency_name"
tools:text="Bitcoin" />
...
<!-- Details -->
<TextView
android:id="@+id/tv_cryptocurrency_detail_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bitcoin"
android:textSize="32sp"
android:transitionName="cryptocurrency_name"
.../>
MainActivity
来自适配器的覆盖方法:
private var tvCryptocurrencyName: TextView? = null
tvCryptocurrencyName = rv_cryptocurrencies.findViewById(R.id.tv_cryptocurrency_name)
override fun onItemClick(cryptocurrency: Cryptocurrency) {
val intent = Intent(this, DetailActivity::class.java)
val options = ActivityOptions.makeSceneTransitionAnimation(this, tvCryptocurrencyName, "cryptocurrency_name")
intent.putExtra(EXTRA_STRING_CRYPTOCURRENCY_DETAILS, cryptocurrency)
startActivity(intent, options.toBundle())
}
在adapter中设置了一个接口,当按下一个item时触发的方法:
interface OnItemClickListener {
fun onItemClick(cryptocurrency: Cryptocurrency)
}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
val cryptocurrency = cryptocurrenciesList[position]
holder.populateUi(cryptocurrency)
holder.itemView.setOnClickListener { onItemClickListener?.onItemClick(cryptocurrenciesList[position]) }
}
当我按查看结果时,应用崩溃并显示消息
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.gabriel.cryptocurrencies, PID: 4416
java.lang.IllegalArgumentException: Shared element must not be null
at android.app.ActivityOptions.makeSceneTransitionAnimation(ActivityOptions.java:694)
at android.app.ActivityOptions.makeSceneTransitionAnimation(ActivityOptions.java:649)
at com.example.gabriel.cryptocurrencies.ui.main.MainActivity.onItemClick(MainActivity.kt:50)
at com.example.gabriel.cryptocurrencies.ui.main.MainAdapter$onBindViewHolder.onClick(MainAdapter.kt:36)
at android.view.View.performClick(View.java:6261)
at android.view.View$PerformClick.run(View.java:23752)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
和其他教程一样,这就是我需要做的。也许我尝试从 RecyclerView
的布局访问字段的方式有误?
编辑
更新但失败的代码。现在,动画似乎会冻结,因为当我按下后退按钮一次时,它不会离开应用程序,只有按下两次。此外,在编辑代码和部署更改时,应用会在尝试切换到详细信息视图后加载详细信息视图。
MainActivity
:
override fun onItemClick(cryptocurrency: Cryptocurrency, field: View) {
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra(EXTRA_CRYPTOCURRENCY_DETAILS, cryptocurrency)
intent.putExtra(EXTRA_TRANSITION_NAME, cryptocurrency.id)
val options = ActivityOptions.makeSceneTransitionAnimation(this, field, cryptocurrency.id)
startActivity(intent, options.toBundle())
}
MainAdapter
:
val tvCryptocurrencyName = holder.itemView.tv_cryptocurrency_name
interface OnItemClickListener {
fun onItemClick(cryptocurrency: Cryptocurrency, field: View)
}
holder.itemView.setOnClickListener { onItemClickListener?.onItemClick(cryptocurrenciesList[position], tvCryptocurrencyName) }
您必须使用 onBindViewHolder
方法为 RecyclerView
适配器中的每个项目设置唯一的转换名称。
像这样:
ViewCompat.setTransitionName(holder.ivIcon, cryptocurrency.id.toString())
通过 Intent
发送 transitionName
,在您的详细信息 activity 中获取此 transitionName
,然后将其设置为您的 View
:
val iconTransitionName = intent.getStringExtra(EXTRA_TRANSITION_NAME)
ivIcon.transitionName = iconTransitionName
更新:
在Adapter
的onBindViewHolder
方法中:
ViewCompat.setTransitionName(holder.itemView, cryptocurrenciesList[position].id)
在onItemClick()
中:
override fun onItemClick(cryptocurrency: Cryptocurrency, textView: TextView) {
val transitionName = ViewCompat.getTransitionName(textView)
val intent = Intent(this, DetailActivity::class.java)
val options = ActivityOptions.makeSceneTransitionAnimation(this, textView, transitionName) // You should send exactly this instace of TextView, not by it's id
intent.putExtra(EXTRA_STRING_CRYPTOCURRENCY_DETAILS, cryptocurrency)
intent.putExtra(EXTRA_STRING_TRANSITION_NAME, cryptocurrency.id.toString())
startActivity(intent, options.toBundle())
}
在DetailActivity
的onCreate()
中:
val transitionName = intent.getStringExtra(EXTRA_STRING_TRANSITION_NAME)
tv_cryptocurrency_detail_name.transitionName = transitionName
在values-v21/styles.xml
中添加:
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="android:windowContentTransitions">true</item>
</style>
并将此添加到您的 TextView
s:
style="@style/MaterialAnimations"
查看简单示例:Github Repo