首页 > 解决方案 > 如何在 RecyclerView 中处理链接到特定卡片的视图持有者?

问题描述

我正在开发一个 android 项目,在该项目中,我想通过向右或向左滑动项目来从垂直回收器视图中删除项目。就删除项目而言,我能够正确地做到这一点。删除项目并致电后出现问题notifyDataSetChanged().

现在在刷新项目时,适配器使用以前的视图持有者来显示卡片。但是在删除一个项目时,我将它的一些布局向左和向右移动。因此,当新项目占据同一个视图持有者时,所有的翻译都会被保留,因此会在屏幕边界之外创建新项目的一些视图(因为我在删除占据该位置的前一个项目时移动了该视图持有者)。

所以我的问题是(以下任何一项的解决方案都将解决我面临的问题),

  1. Java中是否有像c++这样的“免费”功能?这样我就可以释放那个视图持有者。
  2. 如何确保回收站视图不会重用特定的视图持有者?
  3. 如何在所有动画完成之前将布局重置为原始状态?这样我就可以重置所有翻译

谢谢你。

编辑:

下面是适配器和视图支架的代码,如果有人想看看的话。由于视图持有者太大而无法直接理解,所以这里是其中使用的功能的总结:

  1. init -> 只在点击监听器上设置。
  2. updateUI -> 设置更多侦听器并设置卡片中的文本和其他字段。
  3. 当 MotionEvent.ACTION_DOWN 时调用 pressDown ->。
  4. 当 MotionEvent.ACTION_UP 时调用 pressUP ->。
  5. animateViewDisappear -> 动画监听器。
  6. 归档 -> 删除一个元素并告诉适配器数据发生了变化。
  7. pressMove -> 在单击按钮后发生运动时感知。
  8. toggleShowHideView -> 更改某些视图的可见性。
  9. closeOpenedLayout -> 关闭一些展开的布局。

适配器:

class LocalAdapter(var localCardsInfo : ArrayList<LocalModel>?,var fragment: LocalListingFragment) : RecyclerView.Adapter<LocalHolder>() {

    fun refreshDataOnOrientationChange(mLocalCards : ArrayList<LocalModel>?){
        if (localCardsInfo!!.size>0)
            localCardsInfo!!.clear()
        localCardsInfo = mLocalCards
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int {
        return localCardsInfo!!.size
    }

    override fun onBindViewHolder(holder: LocalHolder, position: Int) {
        if (localCardsInfo!=null)
            holder.updateUI(position,localCardsInfo!![position])
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalHolder {
        val card_a : View = LayoutInflater.from(parent.context).inflate(R.layout.card_local_a,parent,false)
        return LocalHolder(card_a,fragment)
    }

}

视图持有人:

class LocalHolder(itemView : View,val fragment: LocalListingFragment) : RecyclerView.ViewHolder(itemView),OpenedLayoutManagerLocal{

    private val TAG = "iotcontrollerapp.@Debug"
    private var _xDelta: Float = 0f
    private var originalDelta : Float = 0f
    private var directionOut1 = false
    private var directionOut = false
    private var previousX = 0f

    var isFavourite : Boolean = false
    var isPropertyPanelOpen : Boolean = false

    val deviceName : TextView = itemView.findViewById(R.id.deviceNameLocal)
    val deviceRoom : TextView = itemView.findViewById(R.id.deviceRoomLocal)
    val deviceOnOff : TextView = itemView.findViewById(R.id.deviceOnOffLocal)

    val showHideLayout : LinearLayout = itemView.findViewById(R.id.showHideLayoutLocal)
    val showHideProperties : TextView = itemView.findViewById(R.id.showHidePropertiesLocal)
    val showHideButton : ImageView = itemView.findViewById(R.id.showHideButtonLocal)

    val favouriteButton : ImageButton = itemView.findViewById(R.id.imageFavouriteButtonLocal)
    val moveButton : MoveableViewImageButton = itemView.findViewById(R.id.imageMoveButtonLocal)

    val changeFragmentToDetail : Button = itemView.findViewById(R.id.changePropertiesLocal)

    val layoutForProperties : LinearLayout = itemView.findViewById(R.id.layoutForPropertyLocal)
    val baseForProperties : LinearLayout = itemView.findViewById(R.id.baseForPropertyLocal)
    private var model : LocalModel? = null
    init {
        itemView.elevation = 0f
        showHideLayout.setOnClickListener({

            isPropertyPanelOpen = !isPropertyPanelOpen
            if (isPropertyPanelOpen){
                if (openedLayoutManagerLocal != this)
                    openedLayoutManagerLocal?.closeOpenedLayout()
                openedLayoutManagerLocal = this
                showHideProperties.text = fragment.getString(R.string.close_property_panel)
                showHideButton.setImageResource(R.drawable.animated_more_button_local)
            }
            else{
                showHideProperties.text = fragment.getString(R.string.open_property_panel)
                showHideButton.setImageResource(R.drawable.animated_more_button_reverse_local)
            }
            val mDrawable = showHideButton.drawable
            if (mDrawable is Animatable)
            {
                (mDrawable as Animatable).start()
            }
            toggleShowHideView()
        })
    }

    fun changeFavouriteButtonState(localModel: LocalModel){
        isFavourite = !isFavourite
        if (isFavourite){
            favouriteButton.setImageResource(R.drawable.avd_heart_fill)
            val a = favouriteButton.drawable
            if(a is Animatable){
                a.start()
            }
            ALL_STATIC_CONSTANTS_AND_METHODS.saveIsFavourite(fragment.activity as Context,localModel.roomName.trim() + "$" + localModel.deviceName,true)
        }
        else{
            favouriteButton.setImageResource(R.drawable.avd_heart_break)
            val a = favouriteButton.drawable
            if(a is Animatable){
                a.start()
            }
            ALL_STATIC_CONSTANTS_AND_METHODS.saveIsFavourite(fragment.activity as Context,localModel.roomName.trim() + "$" + localModel.deviceName,false)
        }
    }

    fun updateUI(position : Int , localModel: LocalModel){
        itemView.elevateLayoutLocal.visibility = View.VISIBLE
        itemView.archiveLayout.visibility = View.VISIBLE

        model = localModel
        originalDelta = itemView.elevateLayoutLocal.x
        changeFragmentToDetail.setOnClickListener({
            fragment.handlerForDetail.sendEmptyMessage(position)
        })
        favouriteButton.setOnClickListener({
            changeFavouriteButtonState(localModel)
        })
        moveButton.setOnTouchListener({v: View?, event: MotionEvent? ->
            if (v == null || event == null) return@setOnTouchListener true
            when (event.action){
                MotionEvent.ACTION_UP -> {
                    v.performClick()
                    pressUP(v,event)
                }
                MotionEvent.ACTION_DOWN -> {
                    pressDown(v,event)
                }
                MotionEvent.ACTION_MOVE -> {
                    pressMove(v,event)
                }
                else -> {
                    Log.e(TAG,"Something happened")
                }
            }
            return@setOnTouchListener true
        })
        isFavourite = (ALL_STATIC_CONSTANTS_AND_METHODS.getIsFavourite(fragment.activity as Context,localModel.roomName.trim() + "$" + localModel.deviceName) ?: false)

        favouriteButton.setImageResource(if (isFavourite) R.drawable.vd_trimclip_heart_full else R.drawable.vd_trimclip_heart_empty)

        deviceRoom.text = localModel.roomName.split("_").dropLast(1).toTypedArray().joinToString(" ")
        deviceName.text = localModel.deviceName.split("_").dropLast(1).toTypedArray().joinToString(" ")
        deviceOnOff.text = if (localModel.isON) fragment.getString(R.string.device_on_off_on) else fragment.getString(R.string.device_on_off_off)

        val components = localModel.componentName.split(" ")
        val value = localModel.value.split(" ")
        val maxValue = localModel.maxValue.split(" ")
        val minValue = localModel.minValue.split(" ")
        if (layoutForProperties.childCount > 0)
            layoutForProperties.removeAllViews()
        if (components.size == value.size && minValue.size == maxValue.size && components.size == minValue.size){
            for (i in 0 until components.size){
                val layout : LinearLayout = LinearLayout.inflate(fragment.activity , R.layout.card_local_b , null) as LinearLayout
                layout.findViewById<TextView>(R.id.propertyLocal).text = components[i].replace("_"," ")
                layout.findViewById<TextView>(R.id.valueLocal).text = value[i]
                layout.findViewById<TextView>(R.id.rangeLocal).text = minValue[i] + " to " + maxValue[i]
                layoutForProperties.addView(layout)
            }

        }

    }

    private fun pressDown(imageButton: View, event: MotionEvent){
        fragment.mLayoutManager?.setScrollEnabled(false)
        openedLayoutManagerLocal?.closeOpenedLayout()
        _xDelta = itemView.elevateLayoutLocal.x - event.rawX
    }

    private fun pressUP(imageButton: View, event: MotionEvent){

        Log.e(TAG,"itemView.elevateLayoutLocal.x :: ${(itemView.elevateLayoutLocal.width / 3.toFloat()) - itemView.elevateLayoutLocal.x}")
        val status = (itemView.elevateLayoutLocal.width / 3.toFloat()) < itemView.elevateLayoutLocal.x
        val status1 = (itemView.elevateLayoutLocal.width / 2.toFloat()) < itemView.elevateLayoutLocal.x
        itemView.elevateLayoutLocal.animate()
                .x(if ((directionOut && status) || (status1 && directionOut1)) itemView.elevateLayoutLocal.width.toFloat() else originalDelta)
                .setDuration(100)
                .setListener(object : Animator.AnimatorListener{
                    override fun onAnimationCancel(animation: Animator?) {
                        if ((directionOut && status) || (status1 && directionOut1)) {
                            itemView.elevateLayoutLocal.visibility = View.GONE
                            val anim = ValueAnimator.ofInt(itemView.archiveLayout.measuredHeight, 0)
                            anim.addUpdateListener { valueAnimator ->
                                val `val` = valueAnimator.animatedValue as Int
                                val layoutParams = itemView.archiveLayout.layoutParams
                                layoutParams.height = `val`
                                itemView.archiveLayout.layoutParams = layoutParams
                            }
                            anim.addListener(animateViewDisappear(this@LocalHolder))
                            anim.duration = 1000
                            anim.start()
                        }else{
                            fragment.mLayoutManager?.setScrollEnabled(true)
                        }
                    }

                    override fun onAnimationEnd(animation: Animator?) {
                        if ((directionOut && status) || (status1 && directionOut1)) {
                            itemView.elevateLayoutLocal.visibility = View.GONE
                            val anim = ValueAnimator.ofInt(itemView.archiveLayout.measuredHeight, 0)
                            anim.addUpdateListener { valueAnimator ->
                                val `val` = valueAnimator.animatedValue as Int
                                val layoutParams = itemView.archiveLayout.layoutParams
                                layoutParams.height = `val`
                                itemView.archiveLayout.layoutParams = layoutParams
                            }
                            anim.addListener(animateViewDisappear(this@LocalHolder))
                            anim.duration = 1000
                            anim.start()
                        }else{
                            fragment.mLayoutManager?.setScrollEnabled(true)
                        }
                    }

                    override fun onAnimationRepeat(animation: Animator?) {

                    }

                    override fun onAnimationStart(animation: Animator?) {

                    }
                }).start()
    }

    class animateViewDisappear(var i : LocalHolder) :Animator.AnimatorListener {
        override fun onAnimationCancel(animation: Animator?) {
            i.itemView.archiveLayout.visibility = View.GONE
            i.itemView.elevateLayoutLocal.visibility = View.GONE
            i.itemView.elevateLayoutLocal.x = i.originalDelta
            val layoutParams = i.itemView.archiveLayout.layoutParams
            layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
            i.itemView.archiveLayout.layoutParams = layoutParams
            i.archived()
        }
        override fun onAnimationEnd(animation: Animator?) {
            i.itemView.archiveLayout.visibility = View.GONE
            i.itemView.elevateLayoutLocal.visibility = View.GONE
            i.itemView.elevateLayoutLocal.x = i.originalDelta
            val layoutParams = i.itemView.archiveLayout.layoutParams
            layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
            i.itemView.archiveLayout.layoutParams = layoutParams
            i.archived()
        }
        override fun onAnimationRepeat(animation: Animator?) {

        }
        override fun onAnimationStart(animation: Animator?) {

        }

    }

    private fun archived(){
        fragment.mLayoutManager?.setScrollEnabled(true)
        if (model != null) {
            ALL_STATIC_CONSTANTS_AND_METHODS.addToArchive(fragment.activity!!, model!!.roomName, model!!.deviceName)
            fragment.mAdapter?.refreshDataOnOrientationChange(LocalDataService.ourInstance.getNonArchivedItems(fragment.activity!!))
        }

    }


    private fun pressMove(imageButton: View, event: MotionEvent){
        directionOut1 = itemView.elevateLayoutLocal.x >= (previousX)
        directionOut = itemView.elevateLayoutLocal.x >= (previousX + 20)
        previousX = itemView.elevateLayoutLocal.x
        fragment.mLayoutManager?.setScrollEnabled(false)
        itemView.elevateLayoutLocal.animate()
                .x(event.rawX + _xDelta)
                .setDuration(0)
                .start()
    }

    fun toggleShowHideView(){
        changeFragmentToDetail.visibility = if (isPropertyPanelOpen) View.VISIBLE else View.GONE
        layoutForProperties.visibility = if (isPropertyPanelOpen) View.VISIBLE else View.GONE
        baseForProperties.visibility = if (isPropertyPanelOpen) View.VISIBLE else View.GONE
    }

    override fun closeOpenedLayout() {
        if (isPropertyPanelOpen){
            isPropertyPanelOpen = !isPropertyPanelOpen
            showHideProperties.text = fragment.getString(R.string.open_property_panel)
            showHideButton.setImageResource(R.drawable.animated_more_button_reverse_local)
            val mDrawable = showHideButton.drawable
            if (mDrawable is Animatable)
            {
                (mDrawable as Animatable).start()
            }
            toggleShowHideView()
        }
    }

    companion object {
        var openedLayoutManagerLocal : OpenedLayoutManagerLocal? = null
    }

}

标签: androidandroid-recyclerviewandroid-viewholder

解决方案


推荐阅读