android - RecyclerView 项目在片段共享元素动画中奇怪地动画
问题描述
我研究了 Android Fragment 共享元素动画。我认为几乎所有工作都是为片段共享元素动画完成的。然而结果太奇怪了。我预计 recyclerview 中的触摸项目将动画到 DetailFragment 中的主要中心图像。动画已启动,但起始位置错误。这个项目有什么问题?
结果动画在这里 https://i.stack.imgur.com/vbxJ5.gif
MainActivity 是这样的
class BodyCheckDemoActivity : AppCompatActivity() , SimpleNavigate{
lateinit var binding: ActivityBodyCheckDemoBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView<ActivityBodyCheckDemoBinding>(this, R.layout.activity_body_check_demo);
}
override fun navigate( fragment : Class<*>, shareElement : List<View>?) {
val fragment : Fragment = supportFragmentManager.fragmentFactory.instantiate(
fragment.classLoader!!,
fragment.name
)
val beginTransaction = supportFragmentManager.beginTransaction()
beginTransaction.addToBackStack(null);
beginTransaction.replace(R.id.fragment_container, fragment )
shareElement?.forEach{ v-> beginTransaction.addSharedElement(v, v.transitionName)}
beginTransaction.commit()
}
override fun onBackPressed() {
super.onBackPressed()
}
private val map = HashMap<String,Any>();
override fun getBundle(): MutableMap<String, Any> = map
}
fun Fragment.onBackPressed() = (activity as? SimpleNavigate)?.onBackPressed()
fun Fragment.navigate( fragment : Class<*> , shareElement : List<View>? = null ) = (activity as? SimpleNavigate)?.navigate(fragment,shareElement)
fun Fragment.getShareMap() = (activity as? SimpleNavigate)?.getBundle()
fun Fragment.getTransitionView( transitionName : String , root : View? = this.view ) : View? {
return root?.let {
if( it.transitionName == transitionName) it
else (it as? ViewGroup)
?.children?.map{ child-> getTransitionView(transitionName, child) }?.find { childView-> childView != null }
}
}
interface SimpleNavigate{
fun onBackPressed()
fun getBundle() : MutableMap<String,Any>
fun navigate( fragment : Class<*>, shareElement : List<View>?)
}
第一个片段是这样的
class BodyCheckMainFragment : Fragment() {
companion object {
fun newInstance() = BodyCheckMainFragment()
}
private lateinit var binding: BodyCheckMainFragmentBinding
val viewModel: BodyCheckMainViewModel by viewModels<BodyCheckMainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initSubscription()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.e( "MotionLayout", "onCreateView")
binding = DataBindingUtil.inflate<BodyCheckMainFragmentBinding>(inflater,R.layout.body_check_main_fragment, container, false)
with(binding.recyclerview){
adapter = MyAdapter(viewModel);
layoutManager = GridLayoutManager(context, 2);
setHasFixedSize(true)
}
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.vm = viewModel
}
private fun initSubscription() {
viewModel.toastMsg.observe( this, Observer {
})
viewModel.toDetail.observe( this, Observer {
getShareMap()!!["resourceId"] = it;
val list = getTransitionView( it.toString() )?.let { arrayListOf( it ) }
getTransitionView( "bg$it" )?.let{ list?.add(it)}
navigate( BodyCheckDetailFragment::class.java , list )
})
}
class BodyCheckMainViewModel : ViewModel() {
private val _toast = MutableLiveData<String>();
private val _detailIndex = MutableLiveData<Int>();
val toastMsg : LiveData<String> = _toast
val toDetail : LiveData<Int> = _detailIndex
fun onSymptopmpsClick( resourceId : Int){
_detailIndex.value = resourceId;
}
fun onRequirementClick( index : Int){
when(index){
0->{ _toast.value = "Mask" }
1->{ _toast.value = "Gloves" }
2->{ _toast.value = "Alcohol" }
3->{ _toast.value = "Soap" }
}
}
}
}
带有相关的 xml 视图
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".bodycheck.BodyCheckDemoActivity"
android:background="@color/white">
...
some views
...
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:layout_constraintTop_toBottomOf="@+id/title_symp"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
和适配器
class BodyState(
val title : String,
val resourceId: Int
)
class BodyStateHolder(
val view: WidgetBodycheckSymptompsBinding,
val itemClick : (BodyState) -> Unit
) : RecyclerView.ViewHolder(view.root){
lateinit var item : BodyState;
fun binding( item : BodyState ){
this.item = item
view.imgSrc = item.resourceId
view.title = item.title
view.bg.transitionName = "bg${item.resourceId}"
view.image.transitionName = "${item.resourceId}"
view.root.setOnClickListener { itemClick(item) }
}
}
class MyAdapter(val viewModel: BodyCheckMainFragment.BodyCheckMainViewModel) : RecyclerView.Adapter<BodyStateHolder>() {
val itemList = arrayListOf(
BodyState( "Fever" , R.drawable.img_face_fever ),
BodyState( "Cough" , R.drawable.img_face_cough ),
BodyState( "Sore troath" , R.drawable.img_face_sore_troath ),
BodyState( "Tiredness" , R.drawable.img_face_headache ),
BodyState( "Aches" , R.drawable.img_face_sore_troath_middle ),
BodyState( "Shortness breath" , R.drawable.img_face_comportable )
)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BodyStateHolder {
return BodyStateHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.widget_bodycheck_symptomps, parent, false )
) {
viewModel.onSymptopmpsClick( it.resourceId )
}
}
override fun getItemCount() = itemList.size;
override fun onBindViewHolder(holder: BodyStateHolder, position: Int) = holder.binding(itemList[position]);
}
第二个fragent在这里
class BodyCheckDetailFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
sharedElementEnterTransition = AutoTransition()
sharedElementReturnTransition = AutoTransition()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate<BodyCheckDetailFragmentBinding>(inflater, R.layout.body_check_detail_fragment, container, false)
binding.srcImg = getShareMap()!!["resourceId"] as Int
binding.faceImage.viewTreeObserver.addOnPreDrawListener(object:ViewTreeObserver.OnPreDrawListener{
override fun onPreDraw(): Boolean {
startPostponedEnterTransition()
binding.faceImage.viewTreeObserver.removeOnPreDrawListener(this)
return true;
}
})
return binding.root
}
}
只有相关的视图 xml
<data>
<variable
name="srcImg"
type="int" />
</data>
<androidx.constraintlayout.motion.widget.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bodyCheckPrimaryColor"
app:layoutDescription="@xml/body_check_detail_fragment_scene">
... some views...
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/face_image"
android:layout_width="260dp"
android:layout_height="240dp"
android:layout_marginTop="80dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:transitionName="@{`bg`+srcImg}">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
tools:src="@drawable/img_face_fever"
android:srcGlide="@{srcImg}"
android:transitionName="@{srcImg+``}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.motion.widget.MotionLayout>
解决方案
推荐阅读
- reactjs - 有没有办法检查用户在 React 中所做的任何状态更改?
- python - 如何正确包装使用 qt 和 shiboken2 的 c++ 库?
- jquery - 表单未提交,单击提交按钮时没有任何反应
- node.js - 如何配置 babelify 将 javascript 文件转换为兼容 IE 11
- python - 从图像坐标反向投影到世界坐标(相对于地图)?
- c# - 如何使用 InOut 参数作为 RefCursor 调用 Postgres V11 存储过程(非函数)?使用 Npgsql v4.0.8
- mongodb - 聚合mongodb中具有多个条件的addToSet
- sql - PL/SQL: ORA-00920: 无效的关系运算符
- python - 如何在 RetrieveUpdateDestroyAPIView 中更改序列化数据的值
- c - 为什么 `ld` 无法链接该程序?