从RecyclerView到片段的共享元素过渡的怪异问题

时间:2019-12-23 16:58:52

标签: android kotlin android-recyclerview shared-element-transition

我们已经在FragmentA和FragmentB之间实现了共享元素转换。在FragmentA上,我们有一个RecyclerView,我们在其中监听单击事件并导航到FragmentB。 RecyclerView可以正常工作,但我们无法解决SharedElementTransition的问题。我们已经在此问题上浪费了超过3天的时间。

SharedElementTransition仅在第一个RecyclerView项上正确设置动画(请参见下面的动画),但是当我单击下面的任何其他项时,动画从第一个项目开始(这是错误的并且看起来很奇怪),并且永远不会显示退出动画。

怎么了?

Video of our problem

我们为每个recyclerview项上的每个视图设置唯一的transitionName,通过view.tag将其传递给FragmentB(SingleReceiptFragment),然后在FragmentB上,将transitionName设置为Fragment的B视图。

RecyclerView适配器:

class ReceiptsAdapter(private val receiptList : List<Receipt>) : RecyclerView.Adapter<ReceiptsAdapter.ViewHolder>(){
    private val dateFormat = SimpleDateFormat("dd/MM/yyyy",Locale.getDefault())
    private var onClickListener : View.OnClickListener? = null
    inner class ViewHolder(val receiptView : View) : RecyclerView.ViewHolder(receiptView),View.OnCreateContextMenuListener{

        init{
            receiptView.setOnCreateContextMenuListener(this)
        }

        override fun onCreateContextMenu(menu: ContextMenu?, p1: View?, p2: ContextMenu.ContextMenuInfo?
        ) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                menu!!.setGroupDividerEnabled(true)
            }

            menu!!.add(1, R.id.addWarranty, adapterPosition, "Preview")
            menu.add(1, R.id.shareReceipt, adapterPosition, "Share")
            menu.add(2, R.id.deleteReceipt, adapterPosition, "Remove")
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val receipt = LayoutInflater.from(parent.context).inflate(R.layout.single_receipt_view,parent,false)
        receipt.setOnClickListener(onClickListener)
        return ViewHolder(receipt)
    }

    override fun getItemCount(): Int = receiptList.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        with(holder.receiptView){
            store_name.transitionName = "storeName$position"
            receipt_date.transitionName = "receiptDate$position"
            payment_type.transitionName = "paymentType$position"
            amount_of_money.transitionName = "amountOfMoney$position"
            arrowIMG.transitionName = "arrowIMG$position"
            store_color.transitionName ="storeColor$position"
        }

        with(holder.itemView){
            val bundle = Bundle()
            bundle.putParcelable("receipt",receiptList[position])
            bundle.putInt("transitionPosition",position)
            tag = bundle
            store_name.text = receiptList[position].storeName
            receipt_date.text = dateFormat.format(Date(receiptList[position].timeAndDate))
            payment_type.text = receiptList[position].paymentType
            amount_of_money.text = BigDecimal(receiptList[position].amountWithVAT).setScale(2, RoundingMode.HALF_EVEN).toString()
        }
        MainActivity.storeColor[receiptList[position].storeName]?.let { color->
            holder.receiptView.store_color.setBackgroundResource(color)
        }
    }

    fun setOnClickListener(listener : View.OnClickListener){
        onClickListener = listener
    }

}

我们的FragmentA:

class HomeFragment : Fragment(R.layout.fragment_home), View.OnClickListener {

    private var currentActivity : FragmentClick? = null
    private val fireStoreCalls : FirestoreCalls by inject()
    private val mainActivityVM : MainActivityViewModel by sharedViewModel()
    private var receiptAdapter : ReceiptsAdapter? =null
    private lateinit var registration : ListenerRegistration
    private var receiptList : MutableList<Receipt>? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        latestReceiptsAdapter.setHasFixedSize(true)
        latestReceiptsAdapter.layoutManager = LinearLayoutManager(context)
        postponeEnterTransition()
        latestReceiptsAdapter.doOnPreDraw {
            startPostponedEnterTransition()
        }

        if(isAdded){
            if(receiptList== null){
                registration = fireStoreCalls.getLatestReceipts(mainActivityVM.firebaseAuth.currentUser!!.uid){
                    if(receiptList == null){
                        receiptList = it.toMutableList()
                        receiptAdapter = ReceiptsAdapter(it)
                        receiptAdapter?.setOnClickListener(this)
                        latestReceiptsAdapter.adapter = receiptAdapter
                    }
                    else{
                        receiptList?.clear()
                        receiptList?.addAll(it)
                        receiptAdapter?.notifyDataSetChanged()
                    }
                }
            }else{
                receiptAdapter = ReceiptsAdapter(receiptList!!)
                receiptAdapter?.setOnClickListener(this)
                latestReceiptsAdapter.adapter=receiptAdapter
            }
        }

    }


    override fun onContextItemSelected(item: MenuItem): Boolean {
        return when (item.itemId){
            R.id.addWarranty -> {
                Toast.makeText(context, "OpenReceipt on position ${item.order}", Toast.LENGTH_SHORT).show()
                true
            }

            R.id.shareReceipt -> {
                Toast.makeText(context, "shareReceipt on position ${item.order}", Toast.LENGTH_SHORT).show()
                true
            }

            R.id.deleteReceipt -> {
                fireStoreCalls.deleteUsersReceipt(mainActivityVM.firebaseAuth.currentUser!!,receiptList!![item.order]){

                }
                Toast.makeText(context, "DeleteReceipt on position ${item.order}", Toast.LENGTH_SHORT).show()
                true
            }
            else -> super.onContextItemSelected(item)
        }

    }

    override fun onDestroyView() {
        super.onDestroyView()
        receiptAdapter = null
        latestReceiptsAdapter.adapter = null
        registration.remove()
    }

    override fun onDetach() {
        super.onDetach()
        currentActivity = null
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        currentActivity = context as? FragmentClick ?: throw IllegalArgumentException("Activity does not implement FragmentClick")
    }

    override fun onClick(v: View?) {

        val singleReceiptFragment = SingleReceiptFragment()
        val moveTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
        singleReceiptFragment.sharedElementEnterTransition = moveTransition
        singleReceiptFragment.enterTransition = AutoTransition()

        sharedElementReturnTransition = moveTransition
        exitTransition = AutoTransition()

        val receivedBundle = v?.tag as? Bundle

        singleReceiptFragment.arguments = receivedBundle

        val transitionPosition = receivedBundle?.getInt("transitionPosition")
        activity?.supportFragmentManager?.beginTransaction()
            //.setCustomAnimations(R.anim.fade_in,R.anim.fade_out)
            ?.addSharedElement(store_name, "storeName$transitionPosition")
            ?.addSharedElement(receipt_date,"receiptDate$transitionPosition")
            ?.addSharedElement(payment_type, "paymentType$transitionPosition")
            ?.addSharedElement(amount_of_money, "amountOfMoney$transitionPosition")
            ?.addSharedElement(arrowIMG, "arrowIMG$transitionPosition")
            ?.addSharedElement(store_color, "storeColor$transitionPosition")
            ?.setReorderingAllowed(true)
            ?.replace(R.id.mainFragment, singleReceiptFragment)
            ?.addToBackStack(null)
            ?.commit()
    }
}

还有我们的FragmentB:

class SingleReceiptFragment : Fragment(){
    private var itemAdapter : ItemAdapter? = null
    private var btnVisibility = View.VISIBLE
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        postponeEnterTransition()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val singleReceiptFragmentDataBinding = DetailedReceiptViewBinding.inflate(layoutInflater)
        singleReceiptFragmentDataBinding.lifecycleOwner = this

        val receipt = arguments?.getParcelable<Receipt>("receipt")
        with(singleReceiptFragmentDataBinding){
            val transitionPosition = arguments?.getInt("transitionPosition")
            root.store_name.transitionName = "storeName$transitionPosition"
            root.receipt_date.transitionName = "receiptDate$transitionPosition"
            root.payment_type.transitionName = "paymentType$transitionPosition"
            root.amount_of_money.transitionName = "amountOfMoney$transitionPosition"
            root.arrowIMG.transitionName ="arrowIMG$transitionPosition"
            root.store_color.transitionName="storeColor$transitionPosition"

            singleReceipt = receipt
            registerForContextMenu(billView)
        }


        exitTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)


        MainActivity.storeColor[receipt?.storeName]?.let {
            receipt?.storeColor = it
            singleReceiptFragmentDataBinding.detailedStoreColor.setBackgroundResource(it)
        }
        receipt?.itemList?.let {
            itemAdapter = ItemAdapter(it)
            with(singleReceiptFragmentDataBinding.root) {
                itemView.layoutManager = LinearLayoutManager(context)
                itemView.setHasFixedSize(true)
                itemView.adapter = itemAdapter
            }
        }

        singleReceiptFragmentDataBinding.expandBtn.setOnClickListener{
            detailedReceipt(btnVisibility)
        }

        singleReceiptFragmentDataBinding.singleReceipt?.amountWithoutVAT = singleReceiptFragmentDataBinding.singleReceipt?.amountWithoutVAT?.let { BigDecimal(it).setScale(8, RoundingMode.HALF_EVEN).toDouble() }!!
        return singleReceiptFragmentDataBinding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        startPostponedEnterTransition()
    }

    override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
        super.onCreateContextMenu(menu, v, menuInfo)
        val inflater = MenuInflater(activity)
        inflater.inflate(R.menu.long_click_receipt, menu)
    }

    override fun onContextItemSelected(item: MenuItem): Boolean {
        return when (item.itemId){
            R.id.addWarranty -> {
                Toast.makeText(context, "addWarranty", Toast.LENGTH_SHORT).show()
                true
            }

            R.id.shareReceipt -> {
                Toast.makeText(context, "shareReceipt", Toast.LENGTH_SHORT).show()
                true
            }

            R.id.deleteReceipt -> {
                Toast.makeText(context, "DeleteReceipt", Toast.LENGTH_SHORT).show()
                true
            }
            else -> super.onContextItemSelected(item)
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        itemView.adapter = null
        itemAdapter = null
        unregisterForContextMenu(bill_view)
    }

}

0 个答案:

没有答案