RecyclerView焦点跳转BUG优化的方法

我们把RecyclerView写成GridView样式,并把RecyclerView的item写成focusable并且有焦点框的时候,我们用焦点滚动RecyclerView的时候会发现RecyclerView的焦点跳转有bug,跟我们想要的焦点跳转规则不一致,会出现的BUG如下图:

黑色方框代表屏幕,我们从左上角的一个item往下按焦点的时候,当需要加载新的一行的时候焦点却跑到了新的一行的最后一个item上面了,(如图,本来是item1获得焦点的,结果跑到item2上面了)。

这是RecyclerView的一个BUG,记得RecyclerView刚出来的时候滚动都还有点卡顿,到了现在滚动起来还是非常流畅的,比较一个全新的艺术般的空间是需要时间来沉淀的,这个BUG我们可以重写GridLayoutManger来解决。直接看代码:

package com.wasu.cs.widget; 

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View; 

/**
 * 自定义GridLayoutManager,修改RecyelerView焦点乱跳的BUG
 * Created by Danxingxi on 2016/4/1.
 */
public class FocusGridLayoutManager extends GridLayoutManager { 

  /**
   * Constructor used when layout manager is set in XML by RecyclerView attribute
   * "layoutManager". If spanCount is not specified in the XML, it defaults to a
   * single column.
   *
   * @param context
   * @param attrs
   * @param defStyleAttr
   * @param defStyleRes
   * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
   */
  public FocusGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  } 

  /**
   * Creates a vertical GridLayoutManager
   *
   * @param context  Current context, will be used to access resources.
   * @param spanCount The number of columns in the grid
   */
  public FocusGridLayoutManager(Context context, int spanCount) {
    super(context, spanCount);
  } 

  /**
   * @param context    Current context, will be used to access resources.
   * @param spanCount   The number of columns or rows in the grid
   * @param orientation  Layout orientation. Should be {@link #HORIZONTAL} or {@link
   *           #VERTICAL}.
   * @param reverseLayout When set to true, layouts from end to start.
   */
  public FocusGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
    super(context, spanCount, orientation, reverseLayout);
  } 

  /**
   * Return the current number of child views attached to the parent RecyclerView.
   * This does not include child views that were temporarily detached and/or scrapped.
   *
   * @return Number of attached children
   */
  @Override
  public int getChildCount() {
    return super.getChildCount();
  } 

  /**
   * Return the child view at the given index
   *
   * @param index Index of child to return
   * @return Child view at index
   */
  @Override
  public View getChildAt(int index) {
    return super.getChildAt(index);
  } 

  /**
   * Returns the number of items in the adapter bound to the parent RecyclerView.
   * @return The number of items in the bound adapter
   */
  @Override
  public int getItemCount() {
    return super.getItemCount();
  } 

  /**
   * Returns the item View which has or contains focus.
   *
   * @return A direct child of RecyclerView which has focus or contains the focused child.
   */
  @Override
  public View getFocusedChild() {
    return super.getFocusedChild();
  } 

  /**
   * Returns the adapter position of the item represented by the given View. This does not
   * contain any adapter changes that might have happened after the last layout.
   *
   * @param view The view to query
   * @return The adapter position of the item which is rendered by this View.
   */
  @Override
  public int getPosition(View view) {
    return super.getPosition(view);
  } 

  /**
   * 获取列数
   * @return
   */
  @Override
  public int getSpanCount() {
    return super.getSpanCount();
  } 

  /**
   * Called when searching for a focusable view in the given direction has failed for the current content of the RecyclerView.
   * This is the LayoutManager's opportunity to populate views in the given direction to fulfill the request if it can.
   * The LayoutManager should attach and return the view to be focused. The default implementation returns null.
   * 防止当recyclerview上下滚动的时候焦点乱跳
   * @param focused
   * @param focusDirection
   * @param recycler
   * @param state
   * @return
   */
  @Override
  public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) { 

    // Need to be called in order to layout new row/column
    View nextFocus = super.onFocusSearchFailed(focused, focusDirection, recycler, state); 

    if (nextFocus == null) {
      return null;
    }
    /**
     * 获取当前焦点的位置
     */
    int fromPos = getPosition(focused);
    /**
     * 获取我们希望的下一个焦点的位置
     */
    int nextPos = getNextViewPos(fromPos, focusDirection); 

    return findViewByPosition(nextPos); 

  } 

  /**
   * Manually detect next view to focus.
   *
   * @param fromPos from what position start to seek.
   * @param direction in what direction start to seek. Your regular {@code View.FOCUS_*}.
   * @return adapter position of next view to focus. May be equal to {@code fromPos}.
   */
  protected int getNextViewPos(int fromPos, int direction) {
    int offset = calcOffsetToNextView(direction); 

    if (hitBorder(fromPos, offset)) {
      return fromPos;
    } 

    return fromPos + offset;
  } 

  /**
   * Calculates position offset.
   *
   * @param direction regular {@code View.FOCUS_*}.
   * @return position offset according to {@code direction}.
   */
  protected int calcOffsetToNextView(int direction) {
    int spanCount = getSpanCount();
    int orientation = getOrientation(); 

    if (orientation == VERTICAL) {
      switch (direction) {
        case View.FOCUS_DOWN:
          return spanCount;
        case View.FOCUS_UP:
          return -spanCount;
        case View.FOCUS_RIGHT:
          return 1;
        case View.FOCUS_LEFT:
          return -1;
      }
    } else if (orientation == HORIZONTAL) {
      switch (direction) {
        case View.FOCUS_DOWN:
          return 1;
        case View.FOCUS_UP:
          return -1;
        case View.FOCUS_RIGHT:
          return spanCount;
        case View.FOCUS_LEFT:
          return -spanCount;
      }
    } 

    return 0;
  } 

  /**
   * Checks if we hit borders.
   *
   * @param from from what position.
   * @param offset offset to new position.
   * @return {@code true} if we hit border.
   */
  private boolean hitBorder(int from, int offset) {
    int spanCount = getSpanCount(); 

    if (Math.abs(offset) == 1) {
      int spanIndex = from % spanCount;
      int newSpanIndex = spanIndex + offset;
      return newSpanIndex < 0 || newSpanIndex >= spanCount;
    } else {
      int newPos = from + offset;
      return newPos < 0 && newPos >= spanCount;
    }
  }
} 

分析:在我们从第五行往下按的时候,第六行的view是重新加载的,当新的一行的item还没有加载出来的时候,去找焦点是找不到的,找不到焦点就会调用mLayout.onFocusSearchFailed()方法,

onFocusSearchFailed方法是LayoutManager的方法,默认是返回null的,我们在自定义GridLayoutManager的时候重写此方法即可,具体的处理步骤请看到代码。在RecyclerView源代码中,onFocusSearchFailed是内部抽象类LayoutManager的一个成员方法,默认返回null。

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

您可能感兴趣的文章:

  • Android RecyclerView的焦点记忆封装
(0)

相关推荐

  • Android RecyclerView的焦点记忆封装

    上一篇中介绍了TV开发中的列表焦点实现 android tv列表焦点记忆实现 ,是用外部代码控制的方式实现的,比较繁琐,现在介绍用自定义RecyclerView的方式来实现,并增加了其他的功能:限制纵向和横向移出焦点,移入移出焦点的事件监听等. 代码实现如下: import android.content.Context; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerVi

  • RecyclerView焦点跳转BUG优化的方法

    我们把RecyclerView写成GridView样式,并把RecyclerView的item写成focusable并且有焦点框的时候,我们用焦点滚动RecyclerView的时候会发现RecyclerView的焦点跳转有bug,跟我们想要的焦点跳转规则不一致,会出现的BUG如下图: 黑色方框代表屏幕,我们从左上角的一个item往下按焦点的时候,当需要加载新的一行的时候焦点却跑到了新的一行的最后一个item上面了,(如图,本来是item1获得焦点的,结果跑到item2上面了). 这是Recycl

  • java内存优化的方法总结

    1.不必要的自动装箱. 自动装箱是将基础数据类型转换为相应的复杂类型,在HashMap的追加删除调查中充满了自动装箱问题,因此尽量避免HashMap代替SparseArray和ArrrayMap等问题. 2.内存复用. 资源复用:通用字符串.颜色.布局. 视图复用:类似于RecyclerView的优化再利用. 目标池:创建目标池,无需重复创建目标,类似于线程池,messae享受元模式. Bitmap对象复用:使用inBitmap属性,Bitmap解码器可以通知Bitmap解码器使用已经存在的存储

  • 解决RecyclerView无法onItemClick问题的两种方法

    对于RecyclerView的使用,大家可以查看将替代ListView的RecyclerView 的使用详解(一),单单从代码结构来说RecyclerView确实比ListView优化了很多,也简化了我们编写代码量,但是有一个问题会导致开发者不会去用它,更比说替换ListView了,我不知道使用过RecyclerView的人有没有进一步查看,RecyclerView没有提供Item的点击事件,我们使用列表不仅仅为了显示数据,同时也可以能会交互,所以RecyclerView这个问题导致基本没有人用

  • JS实现焦点图轮播效果的方法详解

    本文实例讲述了JS实现焦点图轮播效果的方法.分享给大家供大家参考,具体如下: 效果图如下: 一.所用到的知识点 1.DOM操作 2.定时器 3.事件运用 4.Js动画 5.函数递归 6.无限滚动大法 二.结构和样式 <div id="banner" class="banner"> <ul id="list-banner" class="list-banner fn-clear" style="lef

  • jQuery焦点图轮播效果实现方法

    本文实例讲述了jQuery焦点图轮播效果实现方法.分享给大家供大家参考,具体如下: 前面一篇<JS实现焦点图轮播效果的方法详解>详细介绍了JS实现焦点图轮播效果的步骤,这里来分析一下jQuery的相关实现技巧. 核心代码如下: $(function(){ var $next=$(".right"); var $prev=$(".left"); var $list_num=$(".list-num a"); var $banner=$(

  • jquery通过load获取文件的内容并跳到锚点的方法

    本文实例讲述了jquery通过load获取文件的内容并跳到锚点的方法.分享给大家供大家参考.具体分析如下: 昨天在做一个类似于帮助文档型的页面,左边是导航,右边显示的是内容.本来打算右边内容显示区域用iframe来实现,但由于要做iframe的适应高度所以就换了一种方法,使用jquery的ajax中的load方法. 获取远程文件中的内容很容易实现,直接使用jquery的load方法: $("#content").load("xxx.aspx") 这样很容易将xxx.

  • django-rest-swagger的优化使用方法

    如下所示: requirements.txt django==1.10.5 djangorestframework==3.5.3 django-rest-swagger==2.1.1 参考英文文档: http://django-rest-swagger.readthedocs.io/en/latest/ 使用swagger工具结合Django-rest-framework进行restful API的管理以及可视化显示的时候,由于swagger2.1以后不再使用yaml文档描述api,改而使用js

  • python实现提取jira bug列表的方法示例

    目录 公司要求内部每日整理jira bug发邮件,手动执行了一段时间,想着用自动化的方式实现,故用了3天的时间做出了此脚本. 第一版基础版 # -*- coding:utf-8 -*- import requests import re from bs4 import BeautifulSoup as bs import time import os jql = "project = SDP and parent = SDP-13330 AND issuetype in (standardIss

  • .NET使用Collections.Pooled提升性能优化的方法

    目录 简介 Collections.Pooled 如何使用 性能对比 PooledList<T> PooledDictionary<TKey, TValue> PooledSet<T> PooledStack<T> PooledQueue<T> 未手动释放场景 原理解析 总结 简介 性能优化就是如何在保证处理相同数量的请求情况下占用更少的资源,而这个资源一般就是CPU或者内存,当然还有操作系统IO句柄.网络流量.磁盘占用等等.但是绝大多数时候,我

  • Vue首页界面加载优化实现方法详解

    目录 1.路由懒加载 2.js 资源异步加载 3.图片懒加载 4.组件分包懒加载-在视口才加载 1.路由懒加载 问题: 项目在打包时会将首页与其他页面的资源打包到同一个资源文件,造成首页加载的资源文件过大. 解决方法: 路由懒加载:打包时会将每个路由页面拆分成单独的 js 资源,同时跳转到对应页面才会加载对应路由的 js 资源. { path: "/about", name: "about", component: () => import(/* webpac

随机推荐