首页 > 其他分享 >Android TV Recyclerview长按或连续按键,焦点丢失(或者焦点跳跃)

Android TV Recyclerview长按或连续按键,焦点丢失(或者焦点跳跃)

时间:2024-04-02 10:34:57浏览次数:15  
标签:count TV 焦点 lastVisibleItemPos int fromPos Android public View

原因分析

RecyclerView设置适配器后,将数据填充进去,并不会将所有item的view都创建出来,一般只会创建一个屏幕的Item,当长按或者快速按下键时,Recyclerview来不及创建即将获取焦点的view,导致焦点丢失

解决方法

有两种思路:
(1)控制按键速度

 

  这里有两种具体实现策略:

  一种是记录上次按下时间,然后在下次按下时间时去计算与上次按下时间的间隔,如果间隔小于某个值(比如1秒),我们就不处理该次按下事件(具体是在dispatchKeyEvent、或者onKeyDown、或者onKeyUp中return true),

  这里需要注意的是,只有Activity有这些处理方法,Fragment的按键拦截也需要在依附的Activity中进行处理

 

  另一种是是在dispatchKeyEvent、或者onKeyDown、或者onKeyUp中,拿到KeyEvent。通过KeyEvent.getRepeatCount()计算是否是长按,当getRepeatCount()大于0则是长按,值越大表示用户长按事件越长。

  然后我们可以拦截长按事件(同样return true)


(2)对Recyclerview设置LayoutManager,在LayoutManager中控制焦点


  在RecyclerView的LayoutManager中,有这样一个方法onInterceptFocusSearch(View focused, int direction),这个方法就是用于寻找焦点的。当遇到长按或者连续按键焦点飞掉的情况时,需要重载RecyclerView的LayoutManager,重写此方法。

  在实践中有两种具体情况:网格布局和线性布局,其实就是对RecyclerView设置LinearLayoutManager或GridLayoutManager,处理上大同小异。

 

  自定义LinearLayoutManager

public class ScrollControlLayoutManager extends LinearLayoutManager {
private static final String TAG = "ScrollControlLayoutMana";
public ScrollControlLayoutManager(Context context) {
super(context);
}

public ScrollControlLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}

public ScrollControlLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}

@Override
public View onInterceptFocusSearch(View focused, int direction) {
int count = getItemCount();//获取item的总数
int fromPos = getPosition(getFocusedChild());//当前焦点的位置
int lastVisibleItemPos = findLastVisibleItemPosition();//最新的已显示的Item的位置
switch (direction) {//根据按键逻辑控制position
case View.FOCUS_RIGHT:
case View.FOCUS_DOWN:
fromPos++;
break;
case View.FOCUS_LEFT:
case View.FOCUS_UP:
fromPos--;
break;
}

Log.i(TAG, "onInterceptFocusSearch , fromPos = " + fromPos + " , count = " + count+" , lastVisibleItemPos = "+lastVisibleItemPos);
// if(fromPos < 0 || fromPos > count ) {
// //如果下一个位置<0,或者超出item的总数,则返回当前的View,即焦点不动
// return focused;
// } else {
// //如果下一个位置大于最新的已显示的item,即下一个位置的View没有显示,则滑动到那个位置,让他显示,就可以获取焦点了
// if (fromPos >= 0 && fromPos < count && fromPos > lastVisibleItemPos) {
// scrollToPosition(fromPos);
// }
// }

if (fromPos >= 0 && fromPos < count && fromPos > lastVisibleItemPos) {
scrollToPosition(fromPos);
}
return super.onInterceptFocusSearch(focused, direction);
}
}

自定义GridLayoutManager
public class ScrollControlGridLayoutManager extends GridLayoutManager {
private static final String TAG = "ScrollControlLayoutMana";
public ScrollControlGridLayoutManager(Context context, int span) {
super(context, span);
}

public ScrollControlGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}

@Override
public View onInterceptFocusSearch(View focused, int direction) {
int count = getItemCount();//获取item的总数
int fromPos = getPosition(getFocusedChild());//当前焦点的位置
int lastVisibleItemPos = findLastVisibleItemPosition();//最新的已显示的Item的位置
switch (direction) {//根据按键逻辑控制position
case View.FOCUS_RIGHT:
case View.FOCUS_DOWN:
fromPos+=getSpanCount();
break;
case View.FOCUS_LEFT:
case View.FOCUS_UP:
fromPos-=getSpanCount();
break;
}

Log.i(TAG, "onInterceptFocusSearch , fromPos = " + fromPos + " , count = " + count+" , lastVisibleItemPos = "+lastVisibleItemPos);
// if(fromPos < 0 || fromPos > count ) {
// //如果下一个位置<0,或者超出item的总数,则返回当前的View,即焦点不动
// return focused;
// } else {
// //如果下一个位置大于最新的已显示的item,即下一个位置的View没有显示,则滑动到那个位置,让他显示,就可以获取焦点了
// if (fromPos < count && fromPos > lastVisibleItemPos) {
// scrollToPosition(fromPos);
// }
// }

if (fromPos >= 0 && fromPos > lastVisibleItemPos) {
scrollToPosition(fromPos);
}
return super.onInterceptFocusSearch(focused, direction);
}}


总结一下核心逻辑就是在下一次滚动前,计算即将显示的item位置,如果下一个位置大于最新的已显示的item,即下一个位置的View没有显示,
则滑动到那个位置(防止),让他显示,就可以获取焦点了

标签:count,TV,焦点,lastVisibleItemPos,int,fromPos,Android,public,View
From: https://www.cnblogs.com/terrorists/p/18110021

相关文章

  • Android程序员必备的面试技巧!这五个快速码住!
    前言“程序员必备的面试技巧,就像是编写一段完美的代码一样重要。在面试战场上,我们需要像忍者一样灵活,像侦探一样聪明,还要像无敌铁金刚一样坚定。只有掌握了这些技巧,我们才能在面试的舞台上闪耀光芒,成为那个令HR们心动的程序猿!”Android程序员在面试时,除了需要具备扎实的......
  • WPF实现树形表格控件(TreeListView)
    前言本文将探讨如何利用WPF框架实现树形表格控件,该控件不仅能够有效地展示复杂的层级数据,还能够提供丰富的个性化定制选项。我们将介绍如何使用WPF提供的控件、模板、布局、数据绑定等技术来构建这样一个树形表格。一、运行效果1.1默认样式1.2自定义样式二、代码实现......
  • 【Frida】【Android】08_爬虫之网络通信库okhttp3
    ......
  • Android程序员职场规划:让你从职场小白一步一步走进一线公司!
    小公司不可怕,可怕的是一旦业务稳定,你自己不找点事做提升自己。在尽所能的完善公司APP的过程中,你的能力也在锻炼。趁年轻,一定要有闯劲,跳槽这个想法可以有,没必要觉得自己亏欠了谁。对于工作一年到三年的新人来说,想清楚两点就行:第一,当前公司环境确实糟糕,技术上没有提升,职位上......
  • Android Studio安装超详细步骤
    前言在移动互联网时代,作为一个测试工程师,更多的工作也是测试App,在学习了App自动化测试之后,想要实践,就要先安装好一套App自动化测试的环境,第一步就是要安装好AndroidStudio环境,这样就可以使用adb工具,并且使用appium来运行我们的测试脚本。本文就来给大家介绍一下安装androidstud......
  • uniapp 开发之原生Android插件
    开发须知在您阅读此文档时,我们假定您已经具备了相应Android应用开发经验,使用AndroidStudio开发过Android原生。也应该对HTML,JavaScript,CSS等有一定的了解,并且熟悉在JavaScript和JAVA环境下的JSON格式数据操作等。为了插件开发者更方便快捷的开发uni原生插件!2.9.8版本......
  • Android Binder——Java层介绍(三)
    一、简介       对于Android系统,一般是从java层到native层,再到kernel驱动层,形成一个完整的软件架构。Android系统中的BinderIPC通信机制的整体架构也是如此,Java和C++层都定义有同样功能的供应用程序使用的Binder接口。然而Java层中Framework层的......
  • Android+Fragment与Activity之间的信息传递——笔记3
    通过Bundle,Fragment与Activity之间的信息传递protectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn2=findViewById(R.id.btn2);btn3=findViewById(R.......
  • Android-Frida环境部署
    前言什么是Android逆向开发?Android逆向开发是指对已发布的Android应用进行分析和破解,以了解应用程序的内部工作原理,获取应用程序的敏感信息,或者修改应用程序的行为。逆向开发可以帮助开发人员了解他人的代码目录安装ADB安装python和Friday模拟器安装Friday-server......
  • Android 10.0 lowmemorykiller低内存时,禁止某个app被kill掉功能实现
    1.前言在10.0的系统定制化开发中,在对于系统lowmemorykiller低内存的时候,应用保活功能是非常重要的,就是在低内存的情况下禁止某个app被杀掉,所以就需要从lowmemorykiller机制入手,在杀进程的相关流程中进行分析来实现进程避免被杀掉,接下来就来实现这个功能2.lowmemorykiller低......