Android TV 切换日夜模式后,焦点丢失问题 focusableInTouchMode属性

本文介绍了在Android应用中,通过使用`focusableInTouchMode`属性解决日夜模式切换时焦点丢失的问题。作者发现添加此属性可以防止焦点丢失,但可能导致点击事件需要两次触发。解决方案包括使用`setOnTouchListener`替代`setOnClickListener`和处理快速点击事件。
摘要由CSDN通过智能技术生成

问题背景:
1.日夜模式的切换方式:使用uiMode,不重建页面,这样避免了需要页面恢复的问题。
2.焦点丢失的现象:焦点在任意View上,切换日夜模式后,都有比较高的概率偶现焦点丢失,通过addOnGlobalFocusChangeListener监听焦点变化,newFocus直接变为null,而oldFocus就是丢失焦点的view
3.尝试恢复焦点:开始时认为焦点丢失的原因是view在变更属性重绘时,没有绘制完成就设置焦点,导致焦点的丢失,所以进行了一系列恢复焦点的操作,如记录丢失焦点的view然后延时重新requestFocus、或者监听view绘制完成后再延时等等,都没有作用,开始认为是延迟时间不够的原因,后面发现延迟多久requestFocus都不生效,所以可以断定不是重绘导致的问题。

最终方案:

经过测试发现,当给view设置了android:focusableInTouchMode="true"属性后,焦点不会出现丢失的情况了。

focusableInTouchMode属性介绍:

在触摸模式下可以获取焦点。

没加这个属性之前,点击屏幕,是直接触发点击事件的,并且会导致焦点丢失,下次触发keyevent会从默认的初始焦点开始。

设置这个属性之后,点击屏幕,被点击的view会获取焦点,不会触发焦点丢失,但是也带来一个问题,就是第一次点击是触发获取焦点,而不是触发click事件。

所以我理解这个属性,在遥控器控制和触摸屏控制同时生效时,有至关重要的作用。

此时,如果你的事件触发是写在focusChange的事件监听里,就没有问题。但有些业务场景,不是获焦就触发的,是先获焦再点击确认键触发,这时通常实现click事件就能满足需求,但是如果同时需要触摸屏的功能并且希望触摸一次就触发事件,关实现click事件就无法达成目的了。

针对上面的问题解决方案是

  • 1.使用setOnTouchListener代替setOnClickListener
    (原因是,使用focusableInTouchMode=true后会导致click需要点击两次才能触发->第一次会触发焦点)
  • 2.需要响应ok事件,用以代替原来按下ok键自动触发的Click事件
private var sLastClickTime: Long = 0
private const val NO_QUICK_CLICK_INTERVAL: Long = 200
private var isQuickClick = false

@SuppressLint("ClickableViewAccessibility")
@JvmOverloads
fun View.setTVClickListener(needChangeAlpha:Boolean = true, clickListener: () -> Unit){

    setTVClickTouchListener(needChangeAlpha, clickListener)
    setOnKeyListener { v, keyCode, event ->
        if (KeyEventUtil.isOkKeyEvent(keyCode)) {
            if (event.action == KeyEvent.ACTION_UP) {
                v.isPressed = false
                isQuickClick("setTVClickListener ACTION_DOWN isOkKeyEvent"){
                    sLastClickTime = System.currentTimeMillis()
                    clickListener.invoke()
                }
                return@setOnKeyListener true
            }else { //点击事件
                setQuickClick()
                v.isPressed = true
            }
        }
        return@setOnKeyListener false
    }
}
@SuppressLint("ClickableViewAccessibility")
fun View.setTVClickTouchListener(needChangeAlpha:Boolean = true, clickListener: () -> Unit){
    setOnTouchListener { v, event ->
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                setQuickClick()
                if(needChangeAlpha){
                    isQuickClick("setTVClickListener ACTION_DOWN"){v.alpha = 0.5f}
                }
                true
            }
            MotionEvent.ACTION_UP -> {
                isQuickClick("setTVClickListener ACTION_UP"){
                    if(needChangeAlpha){
                        v.alpha = 1.0f
                    }
                    requestFocus(this)
                    sLastClickTime = System.currentTimeMillis()
                    clickListener.invoke()
                }
                true // 返回true表示这个事件被消耗掉了
            }
            else -> false // 其他情况返回false
        }
    }
}
private fun setQuickClick(){
    isQuickClick =  abs(System.currentTimeMillis() - sLastClickTime) < NO_QUICK_CLICK_INTERVAL
}

private fun isQuickClick(tag: String, action: () -> Unit){
    if (!isQuickClick) {
        action.invoke()
    } else {
        LogUtils.e(TAG, "$tag -> quick click")
    }
}
private fun requestFocus(view: View){
    if(!view.hasFocus()){
        view.requestFocus()
    }
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值