Android TV开发的焦点记忆 焦点移动到列表中的某一项中,焦点移出去,在回来时焦点还要定位到原来的item上

前言:网上看了很多博客,各种奇奇怪怪的方法都有,但是并没有什么卵用,写这篇博客主要就是记录一下关于要实现标题所述功能的一种实现方式(提供一种思路)


场景分析

在 Android TV 的开发过程中,经常要跟焦点打交道,焦点问题也是一个很烦人的问题,一个常见的需求是要有焦点记忆功能,也即焦点移动到列表中的某一项中,焦点移出去了,在回来时焦点还要定位到原来的子项上。这种需求常见于是列表是用 listview 或者 recyclerview 实现的。

下面是我画的一个草图:

在这里插入图片描述
简单说一下这个布局:左侧是一个用 RecyclerView 实现的菜单导航栏,右侧是一个随左侧导航栏选项点击变化而切换的 fragment 。

当我现在选中第 6 项按遥控器右键键时,焦点会落在右侧 fragment 左上角第一个控件上,那么此时再按左键,我希望焦点能够继续回到第 6 项这个位置,但根据系统的焦点分发原则,选项 1 会拿到焦点,这就给我带来了困扰,也是这篇博客要解决的问题。

实现思路

当焦点从 RecyclerView 上移出的时候,需要保存 RecyclerView 当前聚焦的 View ,下次 RecyclerVlew 将要获得焦点的时候,主动聚焦到上—次聚焦的 View 上。

听上去很简单,但是实现起来却比较棘手,这里肯定是重写 RecyclerView 会比较方便的,因为要让 RecyclerView 整个控件拿到焦点后,重写分发焦点给子项的逻辑代码。

具体代码实现

  1. 设置RecyclerView在优先于它的子View获取焦点
	public void init(){
        setItemAnimator(null);
        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        setChildrenDrawingOrderEnabled(true);
        this.setFocusable(true);
    }
  1. 重写 RecycleView 的 requestChildFocus()方法记录获得焦点的 View
	@Override
    public void requestChildFocus(View child,View focus){
        if(child!=null){
            super.requestChildFocus(child, focus);
            mLastFocusView = focus;
            //取得获取焦点的item的position
            mLastFocusPosition = getChildViewHolder(child).getAbsoluteAdapterPosition();
        }
    }
  1. 在 RecyclerView 内部焦点切换(相当于重写派发焦点)
	@Override
    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
        LogUtil.i("views = " + views);
        LogUtil.i("lastFocusView = " + mLastFocusView + " mLastFocusPosition = " + mLastFocusPosition);
        if (this.hasFocus() || mLastFocusView == null) {
            //在recyclerview内部焦点切换
            super.addFocusables(views, direction, focusableMode);
        } else {
            //将当前的view放到Focusable views列表中,再次移入焦点时会取到该view,实现焦点记忆功能
            views.add(getLayoutManager().findViewByPosition(mLastFocusPosition));
            LogUtil.i("views.add(lastFocusView)");
        }
    }

当然需要先定义变量来保存上次获得焦点的位置:

	private int mLastFocusPosition = 0;
    private View mLastFocusView = null;

然后在布局文件中将<androidx.recyclerview.widget.RecyclerView>替换成你自定义的这个 MyRecyclerView 就能看到修改完的效果啦


附:如果需要回到上次点击的位置,可以提供 setter 接口,自己在外部为 mLastFocusView 及 mLastFocusPosition 赋值。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android Studio ,当单击列表的某一项后会进入到详细信息界面,需要进行以下步骤: 1. 在布局文件,添加一个 ListView 控件,并设置其 id 和其他属性。例如,可以在 activity_main.xml 添加以下代码: ```xml <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="wrap_content" /> ``` 2. 在 MainActivity.java 文件,创建一个数组或者列表,用于存储列表的数据。例如: ```java String[] items = new String[]{"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"}; ``` 3. 创建一个 ArrayAdapter 对象,用于将数据绑定到 ListView 控件上。例如: ```java ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items); ListView listView = findViewById(R.id.listView); listView.setAdapter(adapter); ``` 4. 在 MainActivity.java 文件,为 ListView 控件设置 OnItemClickListener,以便在用户单击列表响应事件。例如: ```java listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // 进入详细信息界面的代码放在这里 } }); ``` 5. 在 onItemClick 方法,创建一个 Intent 对象,将要进入的详细信息界面的类名传递给 Intent 对象,并使用 startActivity 方法启动该界面。例如: ```java Intent intent = new Intent(MainActivity.this, DetailActivity.class); startActivity(intent); ``` 其,DetailActivity 是你要进入的详细信息界面的类名。 这样,当用户单击列表的某一项,就会进入到详细信息界面了。需要注意的是,详细信息界面的布局和代码需要单独编写。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值