在 recyclerView 列表中,滑动到边界后,继续滑动,会发现自带一个阻尼效果,但是往往不能满足产品需求,需要自定义

比如拉伸的最大距离,或者拉伸的位置

模仿安卓最近任务列表,列表中item可以上下左右滑动,并且左右下方向滑动到边界后会产生阻尼效果,随着拉伸的距离增大而增大

方案1:这里可以自定义阻尼算法,比如粗糙的方式计算

package com.test.recent_task.utils

import android.util.Log
import kotlin.math.max

/**
 * @author liuzhen
 * 拖拽阻尼
 */
class DragDampingHelper {

    companion object {
        private const val TAG = "DragDampingHelper"
    }

    // 最大拉动值
    private var maxNum = 100f

    // 前一次记录位置
    private var previousNum = 0f

    // 起始位置
    private val startNum = 0f

    // 两次移动间移动的相对距离
    private var deltaNum = 0f

    // 结果
    private var result = 0f

    private fun getDampingNum(y: Float, isUp: Boolean): Float {
        // 抬手或者复位时需要清除缓存数据重新计算
        if (isUp || y == 0f) {
            result = 0f
            previousNum = 0f
            return 0f
        }
        deltaNum = y - previousNum
        previousNum = y

        //计算阻尼
        var distance = y - startNum
        if (distance < 0) {
            distance *= -1f
        }

        var damping = (maxNum - distance) / maxNum
        // 1 ~ 0.1
        damping = max(damping, 0.1f)
        if (y - startNum < 0) {
            damping = 1 - damping
        }

        result += deltaNum * damping
        log("getDampingNum previousNum $previousNum deltaNum $deltaNum result $result damping $damping")
        return result
    }

    /** 阻尼拉动 */
    fun pull(y: Float, isUp: Boolean, maxNum: Float): Float {
        this.maxNum = maxNum * 2
        val result = if (y >= 0) getDampingNum(y, isUp) else {
            getDampingNum(0f, false)
            y
        }
        log("pull y $y isUp $isUp result $result")
        return if (result >= maxNum) maxNum else result
    }

    private fun log(str: String) = Log.w(TAG, str)

}
DragDampingHelper

缺点很明显,如果只是单方向滑动,没什么问题,但是如果按下后来回滑动反人类操作,适配难度较大,这里是直接归零处理,只有往一个方向时才开始计算阻尼

方案2:使用 recyclerView 自带的 EdgeEffect

package com.test.recent_task.utils

import android.animation.ValueAnimator
import android.util.Log
import android.view.animation.DecelerateInterpolator
import android.widget.EdgeEffect
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

/**
 * @author liuzhen
 * 滑动阻尼
 */
class DampingEdgeEffect : RecyclerView.EdgeEffectFactory() {

    private var effect: CustomEdgeEffect? = null

    override fun createEdgeEffect(view: RecyclerView, direction: Int) =
        CustomEdgeEffect(view, direction).apply {
            effect = this
        }

    fun up() = effect?.up()

}

class CustomEdgeEffect(private val recyclerView: RecyclerView, private val direction: Int) :
    EdgeEffect(recyclerView.context) {

    companion object {
        const val TAG = "CustomEdgeEffect"
    }

    private val maxEdgeNum = recyclerView.width / 4.5f

    private val upAnim = ValueAnimator().apply {
        duration = 300
        interpolator = DecelerateInterpolator(2.0f)
        addUpdateListener {
            recyclerView.translationX = it.animatedValue as Float
        }
    }

    override fun onPull(deltaDistance: Float, displacement: Float) {
        super.onPull(deltaDistance, displacement)
        handlePull(deltaDistance)
    }

    private fun handlePull(deltaDistance: Float) {
        val transX = recyclerView.translationX
        val sign = if (direction == RecyclerView.EdgeEffectFactory.DIRECTION_RIGHT) -1 else 1
        val moveX = sign * recyclerView.width * deltaDistance
        // 滑动阻尼
        var offset = 1 - abs(transX) / maxEdgeNum
        if (sign < 0 && transX > 0) {
            offset = 1f
        }
        if (sign > 0 && transX < 0) {
            offset = 1f
        }
        // 1 ~ 0.1
        val damping = max(offset, 0.1f)
        val result = transX + moveX * damping
        log("result $result damping $damping moveX $moveX transX $transX sign $sign")
        recyclerView.translationX = min(result, maxEdgeNum)
    }

    fun up() {
        val transX = recyclerView.translationX
        log("up transX $transX")
        if (abs(transX) > 0) {
            upAnim.cancel()
            upAnim.setFloatValues(transX, 0f)
            upAnim.start()
        }
    }

    private fun log(string: String) = Log.d(TAG, string)

}
DampingEdgeEffect

同时自定义 ItemDecoration,必要时还能对滑动方向进行控制

package com.test.recent_task.view.list

import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.Utils
import com.test.recent_task.R
import com.test.recent_task.ext.ViewExt.getDimension

/**
 * @author liuzhen
 * 横向间距
 */
class SpacingItemDecoration : RecyclerView.ItemDecoration() {

    private val leftMargin = Utils.getApp().getDimension(R.dimen.item_margin_left)
    private val rightMargin = Utils.getApp().getDimension(R.dimen.item_margin_right)
    private val centerMargin = Utils.getApp().getDimension(R.dimen.common_dp_28)

    private var scaleNum: Int = 0
    private var touchPosition: Int? = null

    fun updateDecoration(scaleNum: Int, touchPosition: Int) {
        this.scaleNum = scaleNum
        this.touchPosition = touchPosition
    }

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        val itemCount = parent.adapter?.itemCount ?: 0
        val position = parent.getChildAdapterPosition(view)
        if (position == RecyclerView.NO_POSITION) return
        val totalWidth = parent.width
        val itemWidth = view.layoutParams.width
        val otherMargin = (totalWidth - itemCount * itemWidth) / 2
        when {
            itemCount < 3 && position <= 0 -> outRect.set(otherMargin, centerMargin)
            itemCount < 3 && position == itemCount - 1 -> outRect.set(centerMargin, otherMargin)
            position <= 0 -> outRect.set(leftMargin, centerMargin)
            position == itemCount - 1 -> outRect.set(centerMargin, rightMargin)
            else -> outRect.set(centerMargin, centerMargin)
        }

        touchPosition?.let {
            if (it > position) {
                outRect.left += scaleNum
                outRect.right -= scaleNum
            } else if (it < position) {
                outRect.left -= scaleNum
                outRect.right += scaleNum
            }
        }
    }

    private fun Rect.set(left: Int, right: Int) = set(left, 0, right, 0)

}
SpacingItemDecoration

结合effect通过自定义的方式修改Decoration,达到拉伸的阻尼效果

package com.test.recent_task.view

import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.test.recent_task.R
import com.test.recent_task.ScrollLayoutManager
import com.test.recent_task.SpacingItemDecoration
import com.test.recent_task.utils.DampingEdgeEffect
import com.test.recent_task.utils.DragDampingHelper
import kotlin.math.abs

/**
 * @author liuzhen
 * 任务管理器列表
 */
class RecentTaskListView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {

    companion object {
        const val TAG = "RecentTaskListView"
    }

    private val itemDecoration = SpacingItemDecoration(
        resources.getDimension(R.dimen.item_margin_left).toInt(),
        resources.getDimension(R.dimen.item_margin_right).toInt(),
        resources.getDimension(R.dimen.common_dp_28).toInt()
    )

    private var touchView: View? = null
    private val scaleAnim = ValueAnimator().apply {
        repeatCount = 0
        repeatMode = ValueAnimator.RESTART
        duration = 150
        interpolator = AccelerateDecelerateInterpolator()
        addUpdateListener { animator ->
            val num = animator.animatedValue as Float
            updateDecoration(num)
        }
    }

    private val manager = ScrollLayoutManager(context)
    private var maxNum = resources.getDimension(R.dimen.common_dp_150)
    private var threshold = resources.getDimension(R.dimen.common_dp_50)
    private var dDx = 0F
    private var dDy = 0F

    // 用于列表滑动跟抬起时多次触发动效
    private var isUpScale = false

    var removeTaskListener: ((Int) -> Unit)? = null
    var taskClickListener: ((Int) -> Unit)? = null
    var rootClickListener: (() -> Unit)? = null

    private val helper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
        private var swipeBack = false
        private val dragDampingHelper = DragDampingHelper()
        override fun getMovementFlags(
            recyclerView: RecyclerView,
            viewHolder: ViewHolder
        ): Int {
            val swipeFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
            return makeMovementFlags(0, swipeFlags)
        }

        override fun onMove(
            recyclerView: RecyclerView,
            viewHolder: ViewHolder,
            target: ViewHolder
        ): Boolean {
            Log.d(TAG, "onMove position=${viewHolder.adapterPosition}")
            return false
        }

        override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
            val position = viewHolder.adapterPosition
            Log.d(TAG, "onSwiped position=$position data.size=${adapter?.itemCount}")
            if (position >= 0) {
                swipeBack = false
                removeTaskListener?.invoke(position)
            }
        }

        override fun onChildDraw(
            c: Canvas,
            rv: RecyclerView,
            viewHolder: ViewHolder,
            dX: Float,
            dY: Float,
            actionState: Int,
            isCurrentlyActive: Boolean
        ) {
            val isUp = !isCurrentlyActive
            val offDy = dragDampingHelper.pull(dY, isUp, 100f)
            swipeBack = dY > 100
            setCanScroll(abs(offDy) == 0f || !isCurrentlyActive)
            Log.d(TAG, "onChildDraw swipeBack=$swipeBack")
            super.onChildDraw(c, rv, viewHolder, dX, offDy, actionState, isCurrentlyActive)
        }

        override fun convertToAbsoluteDirection(flags: Int, layoutDirection: Int): Int {
            return if (swipeBack) 0 else super.convertToAbsoluteDirection(flags, layoutDirection)
        }

        override fun getSwipeThreshold(viewHolder: ViewHolder) = 0.151f

        override fun getSwipeEscapeVelocity(defaultValue: Float) = 500F
    })

    init {
        addItemDecoration(itemDecoration)
        helper.attachToRecyclerView(this)
        layoutManager = manager
        edgeEffectFactory = DampingEdgeEffect()
        post {
            maxNum = width / 4f
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(e: MotionEvent?): Boolean {
        touch(e)
        return super.onTouchEvent(e)
    }

    private fun touch(e: MotionEvent?) {
        Log.v(TAG, "touch action ${e?.action}")
        e?.apply {
            when (action) {
                MotionEvent.ACTION_DOWN -> {
                    dDx = e.x
                    dDy = e.y
                    touchView = findChildViewUnder(x, y)
                    downScale()
                }

                MotionEvent.ACTION_MOVE -> {
                    // 正常滑动距离
                    val scrollX = dDx - e.x
                    // 滑动超阈值,需要取消动效
                    val isHorizontalScroll = abs(scrollX) > threshold
                    if (isHorizontalScroll && manager.canScrollHorizontally()) {
                        upScale()
                    }
                }

                MotionEvent.ACTION_UP -> {
                    val isScroll = abs(dDx - e.x) < 10 && abs(dDy - e.y) < 10
                    log("click $touchView\ndown x=$dDx y=$dDy, up x=${e.x} y=${e.y}, isScroll $isScroll")
                    (edgeEffectFactory as DampingEdgeEffect).up()
                    setCanScroll(true)
                    upScale()
                    // 滑动时不触发点击
                    if (isScroll) {
                        if (touchView == null) {
                            rootClickListener?.invoke()
                        } else {
                            taskClickListener?.invoke(getChildAdapterPosition(touchView!!))
                        }
                    }
                }
            }
        }
    }

    private fun downScale() {
        log("downScale")
        isUpScale = false
        scaleAnim.cancel()
        touchView?.let {
            scaleAnim.setFloatValues(1f, 0.9f)
            scaleAnim.start()
        }
    }

    private fun upScale() {
        log("upScale isUpScale $isUpScale")
        if (!isUpScale) {
            isUpScale = true
            scaleAnim.cancel()
            touchView?.let {
                scaleAnim.setFloatValues(it.scaleX, 1f)
                scaleAnim.start()
            }
        }
    }

    fun setCanScroll(canScroll: Boolean) {
        log("setCanScroll $canScroll")
        manager.setCanScroll(canScroll)
    }

    /**
     * desc:触摸时更新间距
     * @param scaleNum 缩放度
     */
    private fun updateDecoration(scaleNum: Float) {
        touchView?.apply {
            scaleX = scaleNum
            scaleY = scaleNum
            val touchPosition = getChildAdapterPosition(this)
            log("updateDecoration touchPosition $touchPosition scaleNum $scaleNum")
            val num = (300 * (1 - scaleNum)).toInt()
            itemDecoration.updateDecoration(num, touchPosition)
            invalidateItemDecorations()
        }
    }

    private fun log(str: String) = Log.d(TAG, str)

}
RecentTaskListView

 完整代码如下

package com.test.recent_task.view.list

import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.recyclerview.widget.RecyclerView
import com.test.recent_task.R
import kotlin.math.abs

/**
 * @author liuzhen
 * 任务管理器列表
 */
class RecentTaskListView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {

    companion object {
        private const val TAG = "RecentTaskListView"
    }

    private val itemDecoration = SpacingItemDecoration()

    private var touchView: View? = null
    private val scaleAnim = ValueAnimator().apply {
        repeatCount = 0
        repeatMode = ValueAnimator.RESTART
        duration = 150
        interpolator = AccelerateDecelerateInterpolator()
        addUpdateListener { animator ->
            val num = animator.animatedValue as Float
            updateDecoration(num)
        }
    }

    private val manager = ScrollLayoutManager(context)
    private var autoCancelAnimThreshold = resources.getDimension(R.dimen.common_dp_50)
    private val directionThreshold = resources.getDimension(R.dimen.common_dp_100)
    private val pullMaxThreshold = resources.getDimension(R.dimen.common_dp_140)

    private var dDx = 0F
    private var dDy = 0F
    private var itemDY = 0F
    private var pScrollY = 0F
    private var isUpScale = false
    private var isRemove = false

    var removeTaskListener: ((Int) -> Unit)? = null
    var taskClickListener: ((Int) -> Unit)? = null
    var rootClickListener: (() -> Unit)? = null

    init {
        addItemDecoration(itemDecoration)
        layoutManager = manager
        edgeEffectFactory = DampingEdgeEffect()
        itemAnimator = TransformerAnimator()
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(e: MotionEvent?): Boolean {
        touch(e)
        return super.onTouchEvent(e)
    }

    private fun touch(e: MotionEvent?) {
        e?.apply {
            when (action) {
                MotionEvent.ACTION_DOWN -> {
                    downClick(e.x, e.y)
                    downScaleAnim(e.x, e.y)
                }

                MotionEvent.ACTION_MOVE -> {
                    downClick(e.x, e.y)
                    val diffY = e.y - dDy
                    val diffX = e.x - dDx
                    val canScroll = abs(diffX) > abs(diffY) && abs(diffX) < directionThreshold
                    manager.setScrollHorizontally(canScroll)
                    val isHorizontalScroll = abs(diffX) > autoCancelAnimThreshold
                    if (isHorizontalScroll && manager.canScrollHorizontally()) {
                        upScaleAnim()
                    }
                    if (!manager.canScrollHorizontally()) {
                        val moveY = e.y - itemDY
                        pScrollY += moveY * 0.5f
                        touchView?.translationY = if (pScrollY > 100) 100f else pScrollY
                        itemDY = e.y
                    }
                }

                MotionEvent.ACTION_OUTSIDE, MotionEvent.ACTION_CANCEL -> {
                    removeAnim(0f)
                    manager.reset()
                    pScrollY = 0f
                    itemDY = 0f
                    (edgeEffectFactory as DampingEdgeEffect).up()
                    upScaleAnim()
                }

                MotionEvent.ACTION_UP -> {
                    val isScroll = abs(dDx - e.x) < 10 && abs(dDy - e.y) < 10
                    touchView?.apply {
                        if (-translationY >= pullMaxThreshold) {
                            removeAnim(-rootView.height.toFloat())
                        } else if (isRemove) {
                            isUpScale = false
                            removeTaskListener?.invoke(getChildAdapterPosition(this))
                        } else if (isScroll) {
                            taskClickListener?.invoke(getChildAdapterPosition(this))
                        } else if (abs(translationY) != 0f) {
                            removeAnim(0f)
                        }
                    }
                    manager.reset()
                    pScrollY = 0f
                    itemDY = 0f
                    (edgeEffectFactory as DampingEdgeEffect).up()
                    log("click $touchView\ndown x=$dDx y=$dDy, up x=${e.x} y=${e.y},translationY = $translationY, isScroll $isScroll")
                    if (isScroll && touchView == null) {
                        rootClickListener?.invoke()
                    }
                    upScaleAnim()
                }
            }
        }
    }

    private fun isClickRemove(ivDel: View?, x: Float, y: Float) {
        ivDel?.let {
            val location = IntArray(2)
            it.getLocationOnScreen(location)
            val rangeX = it.width / 4
            val rangeY = it.height / 4
            if (x > location[0] - rangeX && x < location[0] + it.width && y > location[1] - rangeY && y < location[1] + it.height) {
                log("isClickRemove = true")
                isRemove = true
            }
        }
    }

    private fun removeAnim(endY: Float) {
        touchView?.apply {
            animate().translationY(endY).setDuration(150).withEndAction {
                if (endY != 0f) {
                    removeTaskListener?.invoke(getChildAdapterPosition(this))
                }
            }.start()
        }
    }

    private fun downClick(x: Float, y: Float) {
        if (itemDY == 0f) {
            dDx = x
            dDy = y
            itemDY = dDy
            touchView = findChildViewUnder(x, y)
            isUpScale = false
        }
    }

    private fun downScaleAnim(x: Float, y: Float) {
        isRemove = false
        scaleAnim.cancel()
        touchView?.let {
            val ivDel = it.findViewById<View>(R.id.delete_icon)
            isClickRemove(ivDel, x, y)
            scaleAnim.setFloatValues(1f, 0.9f)
            scaleAnim.start()
        }
    }

    private fun upScaleAnim() {
        isRemove = false
        if (!isUpScale) {
            isUpScale = true
            scaleAnim.cancel()
            touchView?.let {
                scaleAnim.setFloatValues(it.scaleX, 1f)
                scaleAnim.start()
            }
        }
    }

    private fun updateDecoration(scaleNum: Float) {
        touchView?.apply {
            scaleX = scaleNum
            scaleY = scaleNum
            val touchPosition = getChildAdapterPosition(this)
            val num = (300 * (1 - scaleNum)).toInt()
            itemDecoration.updateDecoration(num, touchPosition)
            invalidateItemDecorations()
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        log("onDetachedFromWindow")
        scaleAnim.cancel()
        (edgeEffectFactory as DampingEdgeEffect).up()
        removeTaskListener = null
        taskClickListener = null
        rootClickListener = null
        touchView = null
    }

    private fun log(str: String) = Log.i(TAG, str)

}
RecentTaskListView
package com.test.recent_task.view.list

import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.RecyclerView
import java.util.concurrent.CopyOnWriteArrayList

class TransformerAnimator : DefaultItemAnimator() {

    companion object {
        private const val TRANSFORMER_TIME = 300L
    }

    init {
        removeDuration = 0
        changeDuration = TRANSFORMER_TIME
        moveDuration = 0
    }

    private val moveList = CopyOnWriteArrayList<MoveBean>()

    override fun animateRemove(holder: RecyclerView.ViewHolder): Boolean {
        endAnimation(holder)
        dispatchRemoveFinished(holder)
        return false
    }

    override fun runPendingAnimations() {
        if (moveList.isNotEmpty()) {
            moveList.forEach { bean ->
                val view = bean.holder.itemView
                dispatchMoveStarting(bean.holder)
                view.animate().translationX(0f).setDuration(TRANSFORMER_TIME).withEndAction {
                    view.translationX = 0f
                    dispatchMoveFinished(bean.holder)
                    dispatchFinishedWhenDone()
                    moveList.remove(bean)
                }.start()
            }
        }
        super.runPendingAnimations()
    }

    override fun animateMove(
        holder: RecyclerView.ViewHolder,
        fromX: Int,
        fromY: Int,
        toX: Int,
        toY: Int
    ): Boolean {
        val view = holder.itemView
        val foX = fromX + holder.itemView.translationX.toInt()
        endAnimation(holder)
        val deltaX = toX - fromX
        if (deltaX == 0) {
            dispatchMoveFinished(holder)
            return false
        }
        view.translationX = -deltaX.toFloat()
        moveList.add(MoveBean(holder, foX, toX))
        return true
    }

    override fun endAnimation(item: RecyclerView.ViewHolder) {
        item.itemView.translationX = 0f
        item.itemView.translationY = 0f
        moveList.forEach { bean ->
            if (bean.holder === item) {
                dispatchMoveFinished(item)
                moveList.remove(bean)
            }
        }
        super.endAnimation(item)
    }

    private fun dispatchFinishedWhenDone() {
        if (!isRunning) {
            dispatchAnimationsFinished()
        }
    }

    override fun isRunning(): Boolean {
        return !moveList.isEmpty() || super.isRunning()
    }

    private data class MoveBean(
        val holder: RecyclerView.ViewHolder,
        val fromX: Int,
        val toX: Int,
    )

}
TransformerAnimator
package com.test.recent_task.view.list

import android.content.Context
import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager

class ScrollLayoutManager(context: Context) : LinearLayoutManager(context) {

    companion object {
        private const val TAG = "ScrollLayoutManager"
    }

    init {
        orientation = HORIZONTAL
    }

    private var canScrollHorizontally = true
    private var isLock = false

    override fun canScrollHorizontally() = canScrollHorizontally

    fun setScrollHorizontally(canScroll: Boolean) {
        if (!isLock) {
            Log.i(TAG, "setScrollHorizontally $canScroll")
            canScrollHorizontally = canScroll
        }
        isLock = true
    }

    fun reset() {
        isLock = false
        canScrollHorizontally = true
    }

}
ScrollLayoutManager
package com.test.recent_task.adapter

import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.test.recent_task.bean.TaskBean
import com.test.recent_task.databinding.RecentTaskLayoutItemBinding
import com.test.recent_task.holder.TaskViewHolder

class RecentTaskAdapter : RecyclerView.Adapter<TaskViewHolder>() {

    private val mData = mutableListOf<TaskBean>()
    var taskClickListener: ((Int) -> Unit)? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return TaskViewHolder(
            RecentTaskLayoutItemBinding.inflate(inflater, parent, false),
            taskClickListener
        )
    }

    override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
        holder.onBind(mData[position])
    }

    fun getData() = mData

    fun getItem(position: Int): TaskBean? {
        return if (position >= 0 && position < mData.size) {
            mData[position]
        } else null
    }

    @SuppressLint("NotifyDataSetChanged")
    fun setData(data: Collection<TaskBean>) {
        mData.clear()
        mData.addAll(data)
        notifyDataSetChanged()
    }

    override fun getItemCount() = mData.size

    fun removeTask(position: Int): TaskBean? {
        var result: TaskBean? = null
        if (position >= 0 && position < mData.size) {
            result = mData.removeAt(position)
            notifyItemRemoved(position)
        }
        return result
    }

    fun clear() = mData.clear()

}
RecentTaskAdapter
package com.test.recent_task.holder

import androidx.recyclerview.widget.RecyclerView
import com.test.recent_task.Constant
import com.test.recent_task.R
import com.test.recent_task.bean.TaskBean
import com.test.recent_task.databinding.RecentTaskLayoutItemBinding
import com.test.recent_task.ext.ViewExt.setExtBackground
import com.test.recent_task.ext.ViewExt.setExtBackgroundColor
import com.test.recent_task.ext.ViewExt.setExtSrc
import com.test.recent_task.ext.ViewExt.setExtTextColor
import com.test.recent_task.ext.ViewExt.setPXTextSize

class TaskViewHolder(
    val binding: RecentTaskLayoutItemBinding,
    private val clickListener: ((Int) -> Unit)?
) : RecyclerView.ViewHolder(binding.root) {

    init {
        binding.clRoot.post {
            binding.clRoot.pivotX = binding.clRoot.width / 2f
            binding.clRoot.pivotY = binding.clRoot.height / 2f
        }
        binding.groupTitle.setOnClick {
            clickListener?.invoke(adapterPosition)
        }
    }

    fun onBind(bean: TaskBean) {
        binding.apply {
            if (bean.packageName == Constant.PACKAGE_APPLIB || bean.packageName == Constant.PACKAGE_APPLIB_ABROAD) {
                ivImg.setExtSrc(R.drawable.icon_task_applab)
            } else {
                if (bean.thumbnailBmp != null) {
                    ivImg.setImageBitmap(bean.thumbnailBmp)
                } else {
                    ivImg.setExtSrc(R.drawable.icon_task_app_default)
                }
            }
            ivImg.setExtBackgroundColor(R.color.task_item_bg)
            appIcon.setImageBitmap(bean.appLogo)
            appName.text = bean.title
            appName.setExtTextColor(R.color.app_name_color)
            appName.setPXTextSize(R.dimen.app_name_text_size)
            deleteIcon.setExtBackground(R.drawable.icon_delete_task)
        }
    }

}
TaskViewHolder

 

posted on 2025-01-15 16:55  翻滚的咸鱼  阅读(212)  评论(0)    收藏  举报