Kotlin--›Android 超轻量RecyclerView悬停效果(ItemDecoration实现方式,并带touch点击事件)

在这里插入图片描述

逼格特性:

  1. 使用ItemDecoration 实现
  2. 支持悬浮时的 touch事件, 以及Drawable的状态效果
  3. 同样支持子View
  4. 超简单的使用方式, 只需要告诉, 什么位置,需要什么悬停xml即可.

自绘分割线通常需要重写以下方法:

    override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        checkOverDecoration(parent)

        overViewHolder?.let {
            if (!overDecorationRect.isEmpty) {
                addHoverView(it.itemView)

                if (it.itemView.parent != null) {
                    hoverCallback?.drawOverDecoration?.invoke(canvas, paint, it, overDecorationRect)
                }
            }
        }
    }

核心方法

思路: 没次回调draw时候, 遍历RecyclerView界面上, 所有的child, 并且拿到child对应的adapterPosition,
然后通过adapterPosition判断是否需要分割线

如果不需要:
继续往前查找, 直到找到需要分割线位置的child, 拿到adapterPosition , 或者真的没有child需要分割线, 则返回 -1

有了adapterPosition就可以通过adapter创建对应的view, 这个时候, 调用View.Draw方法, 就可以绘制出分割线.

如果不需要 touch 事件, 到这里就实现了, 自定义View的分割线了.

往下查找, 判断紧挨着的下一个child, 是否需要分割线, 用来实现上推效果, 可以通过 itemView.top , 判断是否需要 上推.

  /**
     * 核心方法, 用来实时监测界面上 需要浮动的 分割线.
     * */
    internal fun checkOverDecoration(parent: RecyclerView) {
        childViewHolder(parent, 0)?.let { viewHolder ->
            var firstChildAdapterPosition = viewHolder.adapterPosition

            if (firstChildAdapterPosition != RecyclerView.NO_POSITION) {

                parent.adapter?.let { adapter ->
                    hoverCallback?.let { callback ->

                        var firstChildHaveOver = callback.haveOverDecoration.invoke(firstChildAdapterPosition)

                        if (!firstChildHaveOver) {
                            //第一个child没有分割线, 查找之前最近有分割线的position
                            val findOverPrePosition = findOverPrePosition(firstChildAdapterPosition)
                            if (findOverPrePosition != RecyclerView.NO_POSITION) {
                                //找到了最近的分割线
                                firstChildHaveOver = true

                                firstChildAdapterPosition = findOverPrePosition
                            }
                        }

                        if (firstChildHaveOver) {

                            val overStartPosition = findOverStartPosition(adapter, firstChildAdapterPosition)

                            if (overStartPosition == RecyclerView.NO_POSITION) {
                                clearOverDecoration()
                                return
                            }

                            //创建第一个位置的child 需要分割线
                            val firstViewHolder =
                                callback.createDecorationOverView.invoke(
                                    parent,
                                    adapter,
                                    overStartPosition
                                )

                            val overView = firstViewHolder.itemView
                            tempRect.set(overView.left, overView.top, overView.right, overView.bottom)

                            val nextViewHolder = childViewHolder(parent, findGridNextChildIndex())
                            if (nextViewHolder != null) {
                                //紧挨着的下一个child也有分割线, 监测是否需要上推

                                if (callback.haveOverDecoration.invoke(nextViewHolder.adapterPosition) &&
                                    !callback.isOverDecorationSame.invoke(
                                        adapter,
                                        firstChildAdapterPosition,
                                        nextViewHolder.adapterPosition
                                    )
                                ) {
                                    //不同的分割线, 实现上推效果
                                    if (nextViewHolder.itemView.top < overDecorationRect.height()) {
                                        tempRect.offsetTo(
                                            0,
                                            nextViewHolder.itemView.top - overDecorationRect.height()
                                        )
                                    }
                                }
                            }

                            if (overStartPosition == firstChildAdapterPosition && viewHolder.itemView.top == 0) {
                                //第一个child, 正好是 分割线的开始位置
                                clearOverDecoration()
                            } else {
                                if (overAdapterPosition != overStartPosition) {
                                    clearOverDecoration()

                                    overViewHolder = firstViewHolder
                                    overDecorationRect.set(tempRect)

                                    overAdapterPosition = overStartPosition
                                } else if (overDecorationRect != tempRect) {
                                    overDecorationRect.set(tempRect)
                                }
                            }
                        } else {
                            //当前位置不需要分割线
                            clearOverDecoration()
                        }
                    }
                }
            }
        }
    }

为了支持Touch/Drawable状态的妥协方法

由于分割线是draw出来的, 所以 touch事件是传递不到分割线上的. 即时你通过Recttouch x,y来判断, 也只能实现一部分效果.

Drawable就不好控制了, 里面的子View, 就更不好控制了.

所以…

我这里用了一个骚操作:

将分割线的view, 同时attachwindow上.

    /**
     *  添加悬浮view 到 Activity, 目的是为了 系统接管 悬浮View的touch事件以及drawable的state
     * */
    private fun addHoverView(view: View) {
        if (view.parent == null) {
            windowContent?.addView(
                view, 0,
                FrameLayout.LayoutParams(overDecorationRect.width(), overDecorationRect.height())
            )
        }
    }

再将touch事件, 转发到View到, 就可以完美实现Touch的控制, 和Drawable状态的控制.

    private val itemTouchListener = object : RecyclerView.SimpleOnItemTouchListener() {
        override fun onInterceptTouchEvent(recyclerView: RecyclerView, event: MotionEvent): Boolean {
            val action = event.actionMasked
            if (action == MotionEvent.ACTION_DOWN) {
                isDownInHoverItem = overDecorationRect.contains(event.x.toInt(), event.y.toInt())
            } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
                isDownInHoverItem = false
            }

            if (isDownInHoverItem) {
                onTouchEvent(recyclerView, event)
            }

            return isDownInHoverItem
        }

        override fun onTouchEvent(recyclerView: RecyclerView, event: MotionEvent) {
            if (isDownInHoverItem) {
                overViewHolder?.apply {
                    //一定要调用dispatchTouchEvent, 否则ViewGroup里面的子View, 不会响应touchEvent
                    itemView.dispatchTouchEvent(event)
                    if (itemView is ViewGroup) {
                        if ((itemView as ViewGroup).onInterceptTouchEvent(event)) {
                            itemView.onTouchEvent(event)
                        }
                    } else {
                        itemView.onTouchEvent(event)
                    }
                }
            }
        }
    }

极简使用方式

 HoverItemDecoration().attachToRecyclerView(this) {
   decorationOverLayoutType = {
          R.layout.item_text
      }

      haveOverDecoration = {
          overPositionList.contains(it)
      }
 }

其他部分, 请直接参考源码:

砖厂地址: https://github.com/angcyo/HoverItemDecoration


群内有各(pian)种(ni)各(jin)样(qun)的大佬,等你来撩.

联系作者

点此快速加群

请使用QQ扫码加群, 小伙伴们都在等着你哦!

关注我的公众号, 每天都能一起玩耍哦!

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页