倘伟 发表于 2025-9-26 10:45:24

TV RecyclerView 焦点处理笔记

面对RecyclerView焦点,特别是复杂视图,多类型情况下,需求有时候不按系统定义的走,比如要求首次落焦在第二个,或者焦点移动到边界就不能移动
如果不遵循焦点流程直接粗暴处理,会导致系统分发事件出异常,焦点乱飞
默认焦点使用 addOnChildAttachStateChangeListener 监听
recyclerView.addOnChildAttachStateChangeListener(object :
                RecyclerView.OnChildAttachStateChangeListener {
                override fun onChildViewAttachedToWindow(view: View) {
                  val position = recyclerView.getChildAdapterPosition(view)
                  log("position $position view $view")
                  if (position == 3) view.requestFocus()
                }

                override fun onChildViewDetachedFromWindow(view: View) {

                }
            })View Code对于超出边界时,系统会触发onFocusSearchFailed
    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
                  //...
                } else {
                  focused.rootView.findViewById<View>(R.id.btn_myself)
                }
            }

            else -> super.onFocusSearchFailed(focused, focusDirection, recycler, state)
      }
    }View Code针对焦点移动时自动滚动列表到可见位置,可以使用 onRequestChildFocus
    override fun onRequestChildFocus(
      parent: RecyclerView,
      state: RecyclerView.State,
      child: View,
      focused: View?
    ): Boolean {
      val position = parent.getChildAdapterPosition(child)
      scrollToPosition(position)
      return super.onRequestChildFocus(parent, state, child, focused)
    }View Code如果针对焦点到边界位置后不能移动,或者边界触底动效,可以使用焦点拦截 onInterceptFocusSearch
完整代码
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 < 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(
      parent: RecyclerView,
      state: RecyclerView.State,
      child: View,
      focused: View?
    ): Boolean {
      val position = parent.getChildAdapterPosition(child)
      scrollToPosition(position)
      return super.onRequestChildFocus(parent, state, child, focused)
    }    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焦点
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: TV RecyclerView 焦点处理笔记