在上一次用RecycleView的时候,鄙渣也遇到了焦点问题。可以看看这里:Fragment中的RecycleView的转场动画与保存焦点。
这两个问题详细描述实际上是一样的,鄙渣需要在Acitivity创建完成后立即指定ListView(RecycleView)中的某一个子item获取焦点。在链接中的那篇文章中提到了 viewTreeObserver
这一工具,但是鄙渣最终并没有采用,而是使用了RecycleView.Adapter中的两个方法—— onAttachedToRecyclerView
与 onDetachedFromRecyclerView
两个回调。这两个回调即可判断出RecycleView被初始化和被销毁的时间节点,并进行相关操作。
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
thisRecyclerView = recyclerView
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
thisRecyclerView = null
}
override fun onBindViewHolder(holder: FaqViewHolder, position: Int) {
...
if (position == latestPosition) {
thisRecyclerView?.smoothScrollToPosition(latestPosition)
faqItemContainer.requestFocus()
}
...
faqItemContainer.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
latestPosition = position
}
}
}
companion object {
var latestPosition = 0
}
在这次的问题中,遇到了一个棘手的问题,ListView中没有 onAttachedToRecyclerView
与 onDetachedFromRecyclerView
这两个方法。那么难道又得用 viewTreeObserver
吗?
当然不!上次使用糟糕体验让鄙渣有了十分难受的回忆。所以这次鄙渣选择另寻新方法。在本文一开始已经点明了主要问题——如何判断Activity已经初始化完成 。在这里鄙渣也找到了两个方法,都是用于判断是否初始化完成的。
一个方法来源于Android 判断 activity 初始化是否完成 中提到的,使用WindowToken
来判断是否初始化完成。不过他的写法稍微有些暴力,大概如下:
class XXXActivity : Activity(){
...
private var focusHandler: Handler? = null
...
class RequestListViewFocusHandler(private val weakReference: WeakReference) : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
val activity = weakReference.get()
activity?.let {
if (it.window.decorView.windowToken != null) {
Logger.d("token不为空,请求焦点")
it.listViewChangeAndFocus()
//这里的listView直接是id,kotlin中封装好了这种操作,不需要findviewbyid了
it.listView.setSelection(0)
} else {
sendEmptyMessageDelayed(REFRESH_FOCUS, 100)
Logger.d("token为空")
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
focusHandler = RequestListViewFocusHandler(WeakReference(this))
(focusHandler as RequestListViewFocusHandler).sendEmptyMessageDelayed(REFRESH_FOCUS,100)
...
}
}
这种写法确实能够做到刷新焦点,几乎也能做到实时刷新,但是这种不断发消息轮询的策略总归不大优雅。所以在启硕哥的帮助下,我又发现了第二种方式: onWindowFocusChanged
在关于android activity加载完毕事件这篇文章里其实有说这一类问题的原因了,并且给出的解决方法就是 onWindowFocusChanged
。实现代码如下:
override fun onWindowFocusChanged(hasFocus: Boolean) {
if (hasFocus){
listViewChangeAndFocus()
listView.setSelection(0)
}
}
这样一来就简洁优雅了许多,并且不需要再花里胡哨地找其他的方法了。