【发布时间】:2019-01-20 16:31:41
【问题描述】:
我在 2 个活动(MainActivity 和 DetailActivity)中的 2 个 RecyclerView 项目之间使用默认共享元素转换。从MainActivity 到DetailActivity 的动画效果很好,但是如果用户滚动到DetailActivity 中的新项目,则重新输入动画会将项目移到顶部。我根据需要修改了Android Developers Blog 上共享的示例。这是我的代码的Github Link。我还尝试在 DetailActivity 上禁用退出动画,并尝试将退出动画更改为仅淡入淡出,但这几乎就像根本不尊重退出动画一样。
这是一个视频演示(可以在最后几秒钟看到问题):
MainActivity:
class MainActivity : AppCompatActivity(), ListImageAdapter.ListImageClickListener {
private lateinit var imageData: ImageData
private lateinit var listImageAdapter: ListImageAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupGallery()
prepareTransitions()
}
@SuppressLint("RestrictedApi")
override fun onListImageClick(position: Int, imageView: ImageView) {
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("IMAGE_DATA", imageData)
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(this, imageView,
ViewCompat.getTransitionName(imageView))
startActivityForResult(intent, 101, activityOptions.toBundle())
}
override fun onActivityReenter(resultCode: Int, data: Intent?) {
data?.let { intent ->
if (intent.hasExtra("IMAGE_DATA")) {
imageData = intent.getParcelableExtra("IMAGE_DATA")
listImageAdapter.images = imageData.images
val position = imageData.images.indexOfFirst { it.selected }
itemGallery.scrollToPosition(position)
}
}
super.onActivityReenter(resultCode, data)
}
private fun setupGallery() {
imageData = ImageData(getGalleryItems())
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(itemGallery)
listImageAdapter = ListImageAdapter(imageData.images, this)
itemGallery.adapter = listImageAdapter
itemGallery.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
val selectedView = snapHelper.findSnapView(itemGallery.layoutManager)
selectedView?.let {
val selectedPosition = itemGallery.layoutManager?.getPosition(selectedView)
selectedPosition?.let { onMediumGalleryItemHighlighted(selectedPosition) }
}
}
}
})
}
private fun onMediumGalleryItemHighlighted(position: Int) {
imageData.images = imageData.images.mapIndexed { index, galleryItem ->
when {
index == position -> galleryItem.copy(selected = true)
galleryItem.selected -> galleryItem.copy(selected = false)
else -> galleryItem
}
}
}
private fun prepareTransitions() {
setExitSharedElementCallback(
object : SharedElementCallback() {
override fun onMapSharedElements(names: List<String>?, sharedElements: MutableMap<String, View>?) {
val selectedPosition = imageData.images.indexOfFirst { it.selected }
val selectedViewHolder = itemGallery
.findViewHolderForAdapterPosition(selectedPosition)
if (selectedViewHolder?.itemView == null) {
return
}
sharedElements!![names!![0]] = selectedViewHolder.itemView.findViewById(R.id.listItemImage)
}
})
}
private fun getGalleryItems(): List<Image> {
return listOf(
Image(R.drawable.cat, true),
Image(R.drawable.lion, false),
Image(R.drawable.tortoise, false)
)
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="false"
>
<View android:id="@+id/otherContent"
android:layout_width="match_parent"
android:layout_height="256dp"
android:background="@android:color/holo_green_light"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/itemGallery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_below="@+id/otherContent"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</RelativeLayout>
DetailActivity:
class DetailActivity : AppCompatActivity() {
private lateinit var detailImageAdapter: DetailImageAdapter
private lateinit var imageData: ImageData
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
imageData = intent.extras.getParcelable("IMAGE_DATA")
initViews()
prepareTransitions()
resetScrolledPosition()
}
private fun initViews() {
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(detailGallery)
detailImageAdapter = DetailImageAdapter(imageData.images)
detailGallery.adapter = detailImageAdapter
detailGallery.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
val selectedView = snapHelper.findSnapView(detailGallery.layoutManager)
selectedView?.let {
val selectedPosition = detailGallery.layoutManager?.getPosition(selectedView)
selectedPosition?.let { onItemSelected(selectedPosition) }
}
}
}
})
}
private fun resetScrolledPosition() {
val position = imageData.images.indexOfFirst { it.selected }
imageData.images = imageData.images.mapIndexed { index, galleryItem ->
when {
index == position -> {
galleryItem.copy(selected = true)
}
galleryItem.selected -> galleryItem.copy(selected = false)
else -> galleryItem
}
}
detailImageAdapter.images = imageData.images
detailGallery.scrollToPosition(position)
supportStartPostponedEnterTransition()
}
private fun onItemSelected(position: Int) {
imageData.images = imageData.images.mapIndexed { index, galleryItem ->
when {
index == position -> galleryItem.copy(selected = true)
galleryItem.selected -> galleryItem.copy(selected = false)
else -> galleryItem
}
}
}
override fun onBackPressed() {
var resultIntent = Intent()
resultIntent = resultIntent.putExtra("IMAGE_DATA", imageData)
setResult(Activity.RESULT_OK, resultIntent)
super.onBackPressed()
}
private fun prepareTransitions() {
setEnterSharedElementCallback(
object : SharedElementCallback() {
override fun onMapSharedElements(names: List<String>?, sharedElements: MutableMap<String, View>?) {
val selectedPosition = imageData.images.indexOfFirst { it.selected }
val selectedViewHolder = detailGallery.findViewHolderForAdapterPosition(selectedPosition)
if (selectedViewHolder?.itemView == null) {
return
}
sharedElements!![names!![0]] = selectedViewHolder.itemView.findViewById(R.id.detailItemImage)
}
})
}
}
activity_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:animateLayoutChanges="false"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/detailGallery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</FrameLayout>
【问题讨论】:
-
我不知道我是否错过了它,但我找不到当您从 DetailActivity 返回 MainActivity 时将 SharedElement 更改为新项目的代码。我希望这是有道理的
-
刚刚被审查删除了我的答案 - 尽管它可能是正确的答案 - 只是因为我在那里要求提供更多细节,我不会再浪费时间了......
-
@ArchieG.Quiñones:抱歉,我无法关注。您介意发布答案吗?伪代码也会有所帮助。在 onBackPressed() 我无权访问共享元素。
-
@MartinZeitler 不确定哪些审核政策阻止了您。如果您可以在评论中询问,我可以添加更多详细信息。您是否可以将您的想法概括为要点并在 cmets 中分享链接?如果可行,我可以呼吁 SO 模组接受您的回答。
-
如果你把这个项目的代码分享给我,我会更好地帮助你。我认为这里的问题是,从详细活动返回到 MainActivity 后,您必须将共享元素转换重新设置为正确的项目。
标签: android android-animation android-transitions shared-element-transition