当前位置: 首页 > 编程笔记 >

Android RecyclerView的焦点记忆封装

丌官和泰
2023-03-14
本文向大家介绍Android RecyclerView的焦点记忆封装,包括了Android RecyclerView的焦点记忆封装的使用技巧和注意事项,需要的朋友参考一下

上一篇中介绍了TV开发中的列表焦点实现

android tv列表焦点记忆实现 ,是用外部代码html" target="_blank">控制的方式实现的,比较繁琐,现在介绍用自定义RecyclerView的方式来实现,并增加了其他的功能:限制纵向和横向移出焦点,移入移出焦点的事件监听等。

代码实现如下:

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
public class FocusKeepRecyclerView extends RecyclerView {
  private static final String TAG = FocusKeepRecyclerView.class.getSimpleName();
  //是否可以纵向移出
  private boolean mCanFocusOutVertical = true;
  //是否可以横向移出
  private boolean mCanFocusOutHorizontal = true;
  //焦点移出recyclerview的事件监听
  private FocusLostListener mFocusLostListener;
  //焦点移入recyclerview的事件监听
  private FocusGainListener mFocusGainListener;
  //默认第一次选中第一个位置
  private int mCurrentFocusPosition = 0;

  public FocusKeepRecyclerView(Context context) {
    this(context, null);
  }

  public FocusKeepRecyclerView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public FocusKeepRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    setChildrenDrawingOrderEnabled(true);
    setItemAnimator(null);
    this.setFocusable(true);
  }

  public boolean isCanFocusOutVertical() {
    return mCanFocusOutVertical;
  }

  public void setCanFocusOutVertical(boolean canFocusOutVertical) {
    mCanFocusOutVertical = canFocusOutVertical;
  }

  public boolean isCanFocusOutHorizontal() {
    return mCanFocusOutHorizontal;
  }

  public void setCanFocusOutHorizontal(boolean canFocusOutHorizontal) {
    mCanFocusOutHorizontal = canFocusOutHorizontal;
  }

  @Override
  public View focusSearch(int direction) {
    return super.focusSearch(direction);
  }

  //覆写focusSearch寻焦策略
  @Override
  public View focusSearch(View focused, int direction) {
    Log.i(TAG, "focusSearch " + focused + ",direction= " + direction);
    View view = super.focusSearch(focused, direction);
    if (focused == null) {
      return view;
    }
    if (view != null) {
  //该方法返回焦点view所在的父view,如果是在recyclerview之外,就会是null.所以根据是否是null,来判断是否是移出了recyclerview
      View nextFocusItemView = findContainingItemView(view);
      if (nextFocusItemView == null) {
        if (!mCanFocusOutVertical && (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP)) {
          //屏蔽焦点纵向移出recyclerview
          return focused;
        }
        if (!mCanFocusOutHorizontal && (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT)) {
          //屏蔽焦点横向移出recyclerview
          return focused;
        }
       //调用移出的监听
        if (mFocusLostListener != null) {
          mFocusLostListener.onFocusLost(focused, direction);
        }
        return view;
      }
    }
    return view;
  }

  public void setFocusLostListener(FocusLostListener focusLostListener) {
    this.mFocusLostListener = focusLostListener;
  }

  public interface FocusLostListener {
    void onFocusLost(View lastFocusChild, int direction);
  }

  public void setGainFocusListener(FocusGainListener focusListener) {
    this.mFocusGainListener = focusListener;
  }

  public interface FocusGainListener {
    void onFocusGain(View child, View focued);
  }

  @Override
  public void requestChildFocus(View child, View focused) {
    Log.i(TAG, "nextchild= " + child + ",focused = " + focused);
    if (!hasFocus()) {
      //recyclerview 子view 重新获取焦点,调用移入焦点的事件监听
      if (mFocusGainListener != null) {
        mFocusGainListener.onFocusGain(child, focused);
      }
    }
    super.requestChildFocus(child, focused);//执行过super.requestChildFocus之后hasFocus会变成true
    mCurrentFocusPosition = getChildViewHolder(child).getAdapterPosition();
    Log.i(TAG,"focusPos = "+mCurrentFocusPosition);
  }

 //实现焦点记忆的关键代码
  @Override
  public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
    View view = null;
    if (this.hasFocus() || mCurrentFocusPosition < 0 || (view = getLayoutManager().findViewByPosition(mCurrentFocusPosition)) == null) {
      super.addFocusables(views,direction,focusableMode);
    }else if(view.isFocusable()){
//将当前的view放到Focusable views列表中,再次移入焦点时会取到该view,实现焦点记忆功能
      views.add(view);
    }else{
      super.addFocusables(views,direction,focusableMode);
    }
  }
  
  /**
   * 控制当前焦点最后绘制,防止焦点放大后被遮挡
   * 原顺序123456789,当4是focus时,绘制顺序变为123567894
   * @param childCount
   * @param i
   * @return
   */
  @Override
  protected int getChildDrawingOrder(int childCount, int i) {
    View focusedChild = getFocusedChild();
    Log.i(TAG,"focusedChild ="+focusedChild);
    if(focusedChild== null){
      return super.getChildDrawingOrder(childCount, i);
    }else{
      int index = indexOfChild(focusedChild);
      Log.i(TAG, " index = " + index + ",i=" + i + ",count=" + childCount);
      if(i == childCount-1){
        return index;
      }
      if(i<index){
        return i;
      }
      return i+1;
    }
  }
}

代码实现和注释说明如上。

可以直接作为一个recyclerview使用,已经具有了焦点记忆的功能了,不需要在外层增加额外的代码;要增加限制纵向和横向移出焦点,移入移出焦点的事件监听的功能,可以再调用上面的setXXXListener等方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • A我是Drools的新手,读过一些文档和教程,当然,我的问题有一个简单的解决方案。我使用onle规则文件和类计数器,如下所示。环境为:Wintel JDK 1.7(71),DROOLS 6.1.0 有个规矩 这是kModule 跑步的结果 我的问题是: > 为什么“反淋浴1”规则只涉及最后插入的事实?是否存在任何隐藏行为? 3、为什么在count==1的retract object Counter

  • 问题内容: Java应该没有内存泄漏,但是仍然可能。当我的程序出现内存泄漏时,我可以修复它(我希望)。但是,当某些第三方程序包具备该功能时,我该怎么办?几乎什么都没有,除非不使用此软件包。 还有其他解决方案吗?我喜欢沙盒的想法。您被允许在某个区域内做任何您想做的事情,而您的“身体上的”没有能力打扰其他人。有没有办法为Java中的内存使用创建此类沙箱?想象一下=创建用于内存使用的沙箱,允许某些程序包

  • 问题内容: 我想创建一个程序来模拟Unix服务器上的内存不足(OOM)情况。我创建了这个超级简单的内存消耗者: 它消耗的内存与定义的内存一样多,而现在恰好是50 GB的RAM。它按1 MB分配内存,并精确打印无法分配更多内存的点,这样我就知道它设法吃了哪个最大值。 问题是它有效。即使在具有1 GB物理内存的系统上。 当我检查顶部时,我发现该进程占用了50 GB的虚拟内存,而占用的驻留内存不到1 M

  • 我用Selenium和C#编写了一个多线程自动化,它使用了很多Chrome浏览器(可以重复)。 我尝试使用所有内容,包括“--ingnito”、“--disable-apps ation-cache”,在打开另一个浏览器、、和。这是代码示例: 并建议我的客户通过单击按钮执行此代码来杀死所有“chromedriver.exe”进程: 但是我的客户仍然在说关闭和完成的自动化浏览器的记忆仍然存在? 有什

  • 我想创建一个程序来模拟Unix服务器上的内存不足(OOM)情况。我创造了这个超级简单的记忆食客: 它消耗的内存与中定义的一样多,现在正好是50 问题是它是有效的。即使在具有1的系统上 当我检查top时,我看到这个过程消耗了50 系统规范:Linux内核3.16(Debian)很可能启用了Overmit(不知道如何检查),没有交换和虚拟化。

  • 问题内容: 我有一个程序可以通过递归传递大量数据,例如1000个变量。递归将至少运行50或60次。我担心的是,由于没有足够的空间,是否有可能数据在内存位置上被覆盖,或者如果没有内存,那么我会得到一些例外,即程序内存已经用完(我没有收到这样的错误)? 是否存在错误的解决方案,因为该程序没有更多的内存并且在现有位置上被覆盖? 问题答案: 涉及两个存储区域:堆栈和堆。堆栈是保存方法调用的当前 状态 (即

  • 问题内容: 我的代码中有一些函数,使用记忆很有意义(甚至是强制性的)。 我不想为每个功能分别手动实现。有什么办法(例如在Python中),我可以只使用注释或做其他事情,以便在需要的地方自动获得这些注释? 问题答案: Spring 3.1现在提供了一个注释,它可以做到这一点。 顾名思义,@Cacheable用于划分可缓存的方法-即将结果存储到缓存中的方法,以便在后续调用(具有相同参数)时返回缓存中的

  • 我的想法是有一个这样的东西,您可以在其中存储以前的答案并在以后查找它们。我想这可以通过lambda表达式来完成,但我不太熟悉它们。我不太确定如何编写这个方法,希望得到一些帮助。 用这种方法能做到这一点吗?