找回密码
 立即注册
首页 业界区 安全 TV RecyclerView 焦点处理笔记

TV RecyclerView 焦点处理笔记

倘伟 2025-9-26 10:45:24
面对RecyclerView焦点,特别是复杂视图,多类型情况下,需求有时候不按系统定义的走,比如要求首次落焦在第二个,或者焦点移动到边界就不能移动
如果不遵循焦点流程直接粗暴处理,会导致系统分发事件出异常,焦点乱飞
默认焦点使用 addOnChildAttachStateChangeListener 监听
1.gif
2.gif
  1. recyclerView.addOnChildAttachStateChangeListener(object :
  2.                 RecyclerView.OnChildAttachStateChangeListener {
  3.                 override fun onChildViewAttachedToWindow(view: View) {
  4.                     val position = recyclerView.getChildAdapterPosition(view)
  5.                     log("position $position view $view")
  6.                     if (position == 3) view.requestFocus()
  7.                 }
  8.                 override fun onChildViewDetachedFromWindow(view: View) {
  9.                 }
  10.             })
复制代码
View Code对于超出边界时,系统会触发onFocusSearchFailed
3.gif
4.gif
  1.     override fun onFocusSearchFailed(
  2.         focused: View,
  3.         focusDirection: Int,
  4.         recycler: RecyclerView.Recycler,
  5.         state: RecyclerView.State
  6.     ): View? {
  7.         return when (focusDirection) {
  8.             View.FOCUS_UP -> {
  9.                 if (focused.parent.parent is RecyclerView) {
  10.                     val position = (focused.parent as? View)?.let { getPosition(it) } ?: -1
  11.                     //...
  12.                 } else {
  13.                     focused.rootView.findViewById<View>(R.id.btn_myself)
  14.                 }
  15.             }
  16.             else -> super.onFocusSearchFailed(focused, focusDirection, recycler, state)
  17.         }
  18.     }
复制代码
View Code针对焦点移动时自动滚动列表到可见位置,可以使用 onRequestChildFocus
5.gif
6.gif
  1.     override fun onRequestChildFocus(
  2.         parent: RecyclerView,
  3.         state: RecyclerView.State,
  4.         child: View,
  5.         focused: View?
  6.     ): Boolean {
  7.         val position = parent.getChildAdapterPosition(child)
  8.         scrollToPosition(position)
  9.         return super.onRequestChildFocus(parent, state, child, focused)
  10.     }
复制代码
View Code如果针对焦点到边界位置后不能移动,或者边界触底动效,可以使用焦点拦截 onInterceptFocusSearch
完整代码
7.gif
8.gif
  1. class ScreenMainGridManager(private val mAdapter: BaseAdapter, context: Context) :    GridLayoutManager(context, 4) {    private val marginStart = context.resources.getDimension(R.dimen.common_dp_54)    init {        spanSizeLookup = object : SpanSizeLookup() {            override fun getSpanSize(position: Int): Int {                val viewType = mAdapter.getItemViewType(position)                return when (viewType) {                    ViewType.TITLE.value, ViewType.BANNER.value -> 4                    else -> 1                }            }        }    }    override fun onInterceptFocusSearch(focused: View, direction: Int): View? {        return when (direction) {            View.FOCUS_LEFT -> {                findLeftFocusView(focused) ?: super.onInterceptFocusSearch(focused, direction)            }            View.FOCUS_RIGHT -> {                var isRightFocus = false                findRecyclerView(focused)?.let {                    findRootView(focused)?.let { root ->                        val position = it.getChildAdapterPosition(root)                        if (position > 0 && position + 1 < itemCount) {                            val viewType = mAdapter.getItemViewType(position + 1)                            if (viewType == ViewType.TITLE.value) {                                isRightFocus = true                            }                        } else if (position + 1 == itemCount) {                            isRightFocus = true                        }                    }                }                if (isRightFocus) focused else super.onInterceptFocusSearch(focused, direction)            }            else -> super.onInterceptFocusSearch(focused, direction)        }    }    private fun findLeftFocusView(focused: View): View? {        findRecyclerView(focused)?.let {            val location = IntArray(2)            focused.getLocationOnScreen(location)            if (location[0] < marginStart) {                return focused            }        }        return null    }    private fun findRecyclerView(focused: View): RecyclerView? {        var parent: ViewParent? = focused.parent        for (i in 0 until 2) {            if (parent is RecyclerView) break            parent = parent?.parent        }        return parent as? RecyclerView    }    /** item root view */    private fun findRootView(focused: View): View? {        var result: View? = focused        var parent = focused.parent        for (i in 0 until 2) {            if (parent is RecyclerView) {                return result            }            result = parent as? View            parent = parent?.parent        }        return result    }    override fun onRequestChildFocus(
  2.         parent: RecyclerView,
  3.         state: RecyclerView.State,
  4.         child: View,
  5.         focused: View?
  6.     ): Boolean {
  7.         val position = parent.getChildAdapterPosition(child)
  8.         scrollToPosition(position)
  9.         return super.onRequestChildFocus(parent, state, child, focused)
  10.     }    override fun onFocusSearchFailed(        focused: View,        focusDirection: Int,        recycler: RecyclerView.Recycler,        state: RecyclerView.State    ): View? {        return when (focusDirection) {            View.FOCUS_UP -> {                if (focused.parent.parent is RecyclerView) {                    val position = (focused.parent as? View)?.let { getPosition(it) } ?: -1                    if (position < 0) {                        super.onFocusSearchFailed(focused, focusDirection, recycler, state)                    } else {                        val item = mAdapter.getItem(position)                        if (item is MainBannerItem || item is MeAddItem) {                            focused.rootView.findViewById(R.id.btn_myself)                                ?: focused.rootView.findViewById(R.id.btn_back)                        } else {                            scrollToPosition(0)                            super.onFocusSearchFailed(focused, focusDirection, recycler, state)                        }                    }                } else {                    focused.rootView.findViewById(R.id.btn_myself)                }            }            else -> super.onFocusSearchFailed(focused, focusDirection, recycler, state)        }    }}
复制代码
View Code避免直接request或者clear焦点
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册