導致這個問題出現的原因是由於RecyclerView加載數據並不是將所有的數據加載出來, 快速滑動的時候需要獲取焦點的item並沒有加載出來,這就導致焦點交給系統來處理,這樣就導致了焦點丟失或亂飛的問題。
我看了一下網上的解決思路一般是兩種:
出處: http://blog.csdn.net/Zou_pl/article/details/77507376
- 控制按鍵速度
- 重寫RecyclerView的LayoutManager中的onInterceptFocusSearch接口來控制焦點
第一種方式我覺得是一種取巧的方式,而且具有隨機性(降低體驗度),換句話說不穩定。
第二種方式一般是下面這種形式(針對LinearLayoutManager):
首先我進行測試後發現這種方式並不能解決問題(不起效果), 其次我覺得在onInterceptFocusSearch中調用scrollToPosition這樣會不符合焦點傳遞的流程, 這個思路本意是:在搜尋下一個焦點的時,如果需要獲取焦點的item沒有加載, 那麼調用scrollToPostion來將它顯示出來,那麼它就可以獲取到焦點。 不起作用的原因我覺得還是隨機(不確定性)導致,我們並不能控制正好分發焦點的時候,需要獲取焦點的item加載出來了(顯示),所以就會有不起作用的問題。
我覺得解決這個問題,應該從android的焦點分發處理原理來入手,在TV端我們處理最多的就是KeyEvent,也就是按鍵消息的處理, 所以對於TV端的焦點處理也是和按鍵消息相關。 在TV端移動焦點的流程(假設向右移動):
dispatchKeyEvent(KeyEvent)-->OnKeyListener.onKey->OnKeyDown/OnKeyUp->焦點移動(如果消費掉這個keyEvent)
如果我們不消耗這個移動的keyEvent,那麼會由系統來處理焦點移動,通過View的focusSearch方法找到下一個獲取焦點的View,然後調用requestFocus。
前面我們說過導致長按滑動時焦點亂飛或丟失的原因就是因爲系統來處理焦點移動,所以我們可以通過return true的方式來消耗掉移動的KeyEvent,不讓系統來處理焦點移動。
那麼解決方案就是通過重寫RecyclerView的dispatchKeyEvent接口,當滑動需要加載數據時,return true(消耗事件),不讓系統處理這個移動KeyEvent,這樣焦點就不會交給系統處理。
解決方案如下:
定義ExtendRecyclerView繼承RecyclerView,並重寫dispatchKeyEvent接口:
public class ExtendRecyclerView extends RecyclerView {
public ExtendRecyclerView(Context context) {
super(context);
}
public ExtendRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
int keyCode = event.getKeyCode();
// 這裏只考慮水平移動的情況(垂直移動相同的解決方案)
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
View focusedView = getFocusedChild(); // 獲取當前獲得焦點的view
View nextFocusView;
try {
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
// 通過findNextFocus獲取下一個需要得到焦點的view
nextFocusView = FocusFinder.getInstance().findNextFocus(this, focusedView, View.FOCUS_LEFT);
} else {
// 通過findNextFocus獲取下一個需要得到焦點的view
nextFocusView = FocusFinder.getInstance().findNextFocus(this, focusedView, View.FOCUS_RIGHT);
}
} catch (Exception e) {
nextFocusView = null;
}
// 如果獲取失敗(也就是說需要交給系統來處理焦點, 消耗掉事件,不讓系統處理, 並讓先前獲取焦點的view獲取焦點)
if (nextFocusView == null) {
focusedView.requestFocus();
return true;
}
}
}
return super.dispatchKeyEvent(event);
}
}
這樣就可以解決長按時滑動焦點亂飛或丟失的問題。