让LinearLayout实现焦点记忆
实现这样一个效果,LinearLayout中有若干个Button,当其中任何一个Button上焦后(记这个Button为ButtonA
),将焦点移动到其他地方后再回来LinearLayout中,焦点不管是从什么位置回来LinearLayout,焦点都在ButtonA
。
效果阐述
1. 无焦点记忆效果
为了说明这一点,先看一个普通的情况
在上面的示例中,有左右两列,每列中有四个Button,他们都可以上焦,上焦后字体变红色,丢焦后字体变白色。
上面的示例中,两列的放置非常对称。我们可以看到,最开始在焦点在00
,然后通过右键和下键将焦点移动到13
,我们在13
按钮上按左键,焦点到了03
。当我们在13
按钮上按左键时系统默认找到满意的下一个焦点的位置。
2. 焦点记忆效果
实现如下的焦点记忆效果。
上面的示例中,最开始在焦点在00
,然后通过右键和下键将焦点移动到13
,我们在13
按钮上按左键,焦点到了00
。从而达到焦点记忆。
实现思路
无焦点记忆效果
采用的系统默认焦点寻找办法,如果我们不想系统默认去找下一个焦点该怎么办呢,比较普通的做法是通过设置按钮的nextFocusxxId来实现。
可能稍微细想一下,用这种方式实现焦点记忆效果
,要做的工作会不少,出bug概率还相当大。
如果我们阅读一下View的addFocusables方法,我们就能找到优雅的解决办法。
由此可得,在 Android 中,addFocusables() 方法用于将可获取焦点的视图添加到指定方向的焦点候选视图列表中。通常,这个方法被重写在自定义视图类中,以自定义这些视图的焦点行为。
这个方法的参数包括:
views:要将可获取焦点的视图添加到的列表。
direction:指定焦点搜索的方向。可以是 FOCUS_UP、FOCUS_DOWN、FOCUS_LEFT 或 FOCUS_RIGHT 中的一个。
focusableMode:指示是否仅应添加显式可获取焦点的视图 (FOCUSABLES_ONLY) 或所有视图都可获取焦点 (FOCUSABLES_TOUCH_MODE)。
通过在自定义视图中重写 addFocusables() 方法,可以控制用户在使用方向输入(例如 D-pad 或键盘上的箭头键)浏览您的 UI 时哪些视图有资格接收焦点。
代码实现
接下来我们创建一个FocusRememberLinearLayout
来实现焦点记忆
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
class FocusRememberLinearLayout : LinearLayout {
private var lastFocusedChild: View? = null
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun addFocusables(views: ArrayList<View>, direction: Int, focusableMode: Int) {
if (hasFocus()) { // 自身或者children已经上了焦点
super.addFocusables(views, direction, focusableMode)
} else {
// 实现焦点记忆
lastFocusedChild?.addFocusables(views, direction, focusableMode)
?: run { super.addFocusables(views, direction, focusableMode) }
}
}
override fun requestChildFocus(child: View?, focused: View?) {
// 记录最新的焦点控件
lastFocusedChild = focused
super.requestChildFocus(child, focused)
}
}