电视版智家App兼容触摸和遥控器交互方式

1. 背景

电视版智家App一直运行在海尔电视上,使用遥控器进行交互,后来添加了一个指向型遥控器,也是通过焦点进行交互的,一直运行正常。

现在增加了一款新的电视,此电视竟然可触摸,太厉害了,超级好用。但是我们的App跑上去后竟然水土不服,交互出现了问题,问题有很多,有UI适配的,有交互的,有横竖屏的,今天先研究一下交互模式,饭要一口一口吃。

2. 焦点与触摸模式

2.1 什么是焦点?它有什么作用?

在非触屏的手机或者电视上,我们通常需要用键盘、鼠标、遥控器与界面进行交互,当交互的时候必须先使目标控件获得焦点(比如高亮起来),这样用户才会注意到是什么控件接受输入。

而如果是在触屏时代,用户可以直接用手指点击控件,这个时候就没必要将目标高亮了(即获取焦点)。

总结,焦点主要用于非触屏手机上的,是与用户交互有关的产物,跟交互方式紧密相关(当然它还有其它用途,这个后续再说,现在先聚焦在这里)。

2.2 触摸模式

2.2.1 触摸模式是什么?

现在的手机大多是触摸屏,使用起来很方便,它与传统的键盘、鼠标、遥控器的交互方式不太一样的一点是,它不需要焦点辅助交互,更加方便人们使用手机,所以它是一种新的交互模式,我们称之为"触摸模式"(Touch Mode)。

在Android上,有一个方法(View#isInTouchMode() )用来指示当前是否是处于触摸模式,它是用户和手机进行交互时view层次结构的一个状态。代表了最近一次的交互是否是通过触摸屏发生的,因为在Android设备上还存在别的交互方式,比如键盘、鼠标等等。

2.2.2 进入/退出触摸模式,如何判断当前是什么模式

Android系统会自动维护当前的状态,即是否是触摸模式。对于支持触摸功能的设备,当用户触摸屏幕时,设备会立即进入触摸模式。无论何时,只要用户点击方向键或滚动轨迹球或点击遥控器,设备就会退出触摸模式并找到一个视图使其获得焦点。

整个系统(所有窗口和 Activity)都将保持同一个触摸模式的状态。可通过调用View#isInTouchMode() 来检查设备目前是否处于触摸模式,用于您自己的逻辑处理时使用。

2.3 触摸模式与焦点

前面说了,触摸模式是与传统的遥控器、键盘不一样的一种交互方式,它是不需要焦点的,任何已聚焦的控件当进入触摸模式时都变为未聚焦状态。而当使用鼠标和键盘时,就会立即离开触摸模式,控件就会变成聚焦的状态。

那么是不是触摸模式下就没有焦点的概念了呢?

非也,上边只是说不需要焦点进行辅助交互,但还是有其它场景是需要焦点的,比如文本输入框。这就引出了两个概念,搞清了它俩,也就弄清了焦点和触模模式了,它们是setFocusableInTouchMode 和 setFocusable 。

2.3.1 setFocusableInTouchMode 和 setFocusable 方法

  • setFocusable:设置控件是否能获取焦点。可以通过isFocusable()获取其状态。
  • setFocusableInTouchMode:在触摸模式下,设置控件是否允许聚焦。可以通过isFocusableInTouchMode() 获取其状态。

2.3.2 setFocusableInTouchMode 和 setFocusable 的使用场景

  1. 在使用键盘、鼠标、轨迹球、遥控器的情况下,只有setFocusable为true的控件,才可以获取焦点(选中时高亮)。
  2. 在触摸模式下,setFocusable为true,并无法保证控件可以获取焦点,它只能保证在非触摸模式下,该控件可以允许获取焦点。如果想在在触摸模式中,改变控件是否允许聚焦,需要使用setFocusableInTouchMode。
  3. 从上面我们也可以看出,不管是否在触摸模式下,控件获取焦点的前提是isFocusable()为true。而在触摸模式下,只有isFocusable()和isFocusableInTouchMode()都为ture的情况下,控件才允许聚焦。

2.3.3 调用 setFocusableInTouchMode 和 setFocusable 对相互的影响

  1. setFocusableInTouchMode为true,会使isFocusable也变为true,而setFocusableInTouchMode为false并不影响isFocusable。
  2. setFocusable为false,会使isFocusableInTouchMode变为false,而setFocusable为true并不影响isFocusableInTouchMode

2.3.4 各种常用控件的默认初始状态

控件 Focusable FocusableInTouchMode Clickable LongClickable
View FALSE FALSE FALSE FALSE
TextView FALSE FALSE FALSE FALSE
EditText TRUE TRUE TRUE TRUE
Button TRUE FALSE TRUE FALSE
ImageButton TRUE FALSE TRUE FALSE
ImageView FALSE FALSE FALSE FALSE
CheckBox TRUE FALSE TRUE FALSE
RadioButton TRUE FALSE TRUE FALSE
ProgressBar FALSE FALSE FALSE FALSE
LinearLayout FALSE FALSE FALSE FALSE
RelativeLayout FALSE FALSE FALSE FALSE
其他Layout都几乎一样 FALSE FALSE FALSE FALSE

从上面我们可以看出,大部分的控件FocusableInTouchMode属性都为false。只有类似EditText这种控件才为true,因为EditText需要提供在没用户点击的条件下,弹出一个软键盘进行输入的功能。

3. 智家App适配触摸屏要点

由于电视之前只有遥控器,所以它的交互方式一直是依赖焦点的,新电视支持触摸,这块肯定是需要适配的,经过上面的研究,发现现在的问题主要是使用focusableInTouchMode属性不当导致的,需要检查一下所有页面中的控件,此属性的使用情况,是否正确。

以下总结了在电视上适配触摸屏的要点及可能会出现的问题:

  1. 触摸模式的进入和退出系统已做好了,不用开发者操心;

  2. 开发者只需要管理好控件的focusableInTouchMode属性即可,一般情况(不需要输入时)下将它设置为false即可,可在xml中进行设置也可以通过 setFocusableInTouchMode() 在代码中设置;

  3. 当需要根据当前的模式进行自定义逻辑适配时,可使用方法:View#isInTouchMode()

  4. 可能会出现的问题需要注意:

    1. 普通的控件或自定义控件,滥用了setFocusableInTouchMode,会出现点击一下获取焦点,再点一下才是点击的情况。对于这种情况,将它的setFocusableInTouchMode设置为false即可;
    2. 以后开发时,要小心onTouchEvent()方法,因为它只会在触摸屏上才有,在非触摸屏上永远不会触发它,如果使用了此方法,到时候逻辑可能就不正常了;

4. 统一适配View

在实际使用过程中,我们需要将常用的一些适配放到基类中进行统一适配,可方便我们的开发及维护,这里我们统一做了几处适配。

4.1 封装方法判断是否是移动屏

kotlin 复制代码
fun tvProductTypeIsPad(): Boolean {
    return getTvProductType() == TvProductType.PAD
}

4.2 HoverViewExtension

通用扩展类,用于在鼠标/指向型遥控器移动到View时,智能修改focuseableInTouchMode,以适配移动屏和普通电视,关键代码如下所示

kotlin 复制代码
fun  View.addHoverListener(autoChangeItemFocusableInTouchMode: Boolean = true) {
    this.setOnGenericMotionListener { v, event ->
        when(event?.action) {
            MotionEvent.ACTION_HOVER_ENTER -> {
                if (autoChangeItemFocusableInTouchMode) {
                    v.isFocusableInTouchMode = true
                }
            }
            MotionEvent.ACTION_HOVER_EXIT -> {
                if (autoChangeItemFocusableInTouchMode) {
                    v.isFocusableInTouchMode = false
                }
            }
        }
        return@setOnGenericMotionListener true
    }
}

4.3 UiUtils

创建Utils方法,实现点击时的动效,点击缩小View,松开手指恢复原大小,如下

kotlin 复制代码
fun commonCardTouchScale(
    view: View?,
    scaleX: Float = SCALE_RATIO_SMART,
    scaleY: Float = SCALE_RATIO_SMART,
    duration: Long = SCALE_DURATION_SMART,
    effectZ: Boolean = false
) {
    view?.setOnTouchListener { v, event ->
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                if (effectZ) view.elevation += 1
                scaleView(v,
                    getSmartScaleRatio(scaleX, v),
                    getSmartScaleRatio(scaleY, v),
                    getSmartScaleDuration(duration, event.action)
                )
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                if (effectZ) view.elevation -= 1
                scaleView(v, SCALE_DEFAULT, SCALE_DEFAULT,
                    getSmartScaleDuration(duration, event.action)
                )
            }
        }
        false
    }
}

fun scaleView(
    view: View,
    scaleX: Float,
    scaleY: Float,
    duration: Long = SCALE_DURATION
) {
    view.animate().scaleX(scaleX).scaleY(scaleY).setDuration(duration)
        .setInterpolator(AccelerateDecelerateInterpolator())
        .start()
}

如上,实现了一些通用的方法,然后在每个页面的合适位置中进行适配,即可完成整个工程的适配。当然适配工作是一项细致的事情,需要细心修改,并反复打磨,最后呈现出一个完美的作品。

5. 总结

本篇文章梳理总结了焦点、触摸模式的定义,维护是否是触摸模式的方式、focusable和focusableInTouchMode属性的区别,最后梳理出智家App适配触摸屏的要点,清晰了概念,根据要点,走查一遍App功能,大体上就算适配完了,后续再根据需要进行一些UI或其它逻辑的完善即可。

6. 参考

Android 触摸模式(Touch Mode)

7. 团队介绍

「三翼鸟数字化技术平台-应用软件框架开发」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。

相关推荐
愤怒的代码1 天前
深入解析 Binder 运行的状态
android·app
Bigger2 天前
Tauri (23)——为什么每台电脑位置显示效果不一致?
前端·rust·app
Bigger2 天前
Tauri (22)——让 `Esc` 快捷键一层层退出的分层关闭方案
前端·react.js·app
愤怒的代码5 天前
解析Android内存分析的指标
android·app
iOS阿玮7 天前
1V1 社交精准收割 3.6 亿!40 款马甲包 + 国内社交难度堪比史诗级!
uni-app·app·apple
Bigger7 天前
Tauri(21)——窗口缩放后的”失焦惊魂”,游戏控制权丢失了
前端·macos·app
Bigger7 天前
Tauri (20)——为什么 NSPanel 窗口不能用官方 API 全屏?
前端·macos·app
iOS阿玮8 天前
想偷懒购买现成的应用,结果一更新就遇到了4.3a!
uni-app·app·apple
方白羽8 天前
Android 中Flags从源码到实践
android·app·客户端