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掃碼加羣, 小夥伴們都在等着你哦!

關注我的公衆號, 每天都能一起玩耍哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章