如何将快速卷轴添加到RecyclerView


73

背景

在ListView上,您可以使用快速滚动器,该滚动器允许您拖动滚动条以轻松滚动至所需位置(使用fastScrollEnabled属性)

连同“ SectionIndexer ”类以及可选的一些属性一起,您可能会在使用此滚动条时看到一个漂亮的弹出窗口(此处链接)。

这样的事情会显示在通讯录应用程序中,以便您可以轻松滚动至特定字母。

问题

RecyclerView似乎没有任何这些。甚至没有快速滚动。

问题

如何为RecyclerView添加快速滚动器功能?

Answers:


52

几天前,当我遇到这种情况时,我偶然发现了这个问题。这是FastScroll for RecyclerView的示例实现

github.com/danoz73/RecyclerViewFastScroller

尝试运行示例应用程序,并仔细阅读代码,以查看简单的RecyclerViewFastScroller小部件的相当简单的用法。有关github的信息,但是我将在此处包括垂直快速滚动器的基本用法。

有关完整示例,请参阅repo中示例应用程序

基本用法

在RecyclerView所在的活动或片段XML中,包含VerticalRecyclerViewFastScroller对象。以下示例将处于相对布局中:

...
  <android.support.v7.widget.RecyclerView
      android:id="@+id/recyclerView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>


  <xyz.danoz.recyclerviewfastscroller.vertical.VerticalRecyclerViewFastScroller
      android:id="@+id/fast_scroller"
      android:layout_width="@dimen/however_wide_you_want_this"
      android:layout_height="match_parent"
      android:layout_alignParentRight="true"
    />
...

在以编程方式设置布局的片段或活动中,将快速滚动器连接到回收站:

...
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      View rootView = inflater.inflate(R.layout.recycler_view_frag, container, false);
      ...

      // Grab your RecyclerView and the RecyclerViewFastScroller from the layout
      RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
      VerticalRecyclerViewFastScroller fastScroller = (VerticalRecyclerViewFastScroller) rootView.findViewById(R.id.fast_scroller);

      // Connect the recycler to the scroller (to let the scroller scroll the list)
      fastScroller.setRecyclerView(recyclerView);

      // Connect the scroller to the recycler (to let the recycler scroll the scroller's handle)
      recyclerView.setOnScrollListener(fastScroller.getOnScrollListener());

      ...
      return rootView;
  }
...

希望这可以帮助!

编辑:现在增加了对Android-Lollipop-Contacts样式的部分指示器的支持!查看示例应用程序的实现以获取详细信息。


1
真好 虽然不喜欢项目的颜色:)。它可以以某种方式处理SectionIndexer吗?例如,要在Lollipop的通讯录应用程序上显示气泡,请显示当前字母?
Android开发人员

我将在本周添加类似的其他实现!只是想先展示一个基本示例:)
丹尼尔·史密斯

好的,我将为您提供奖励,但是我仍然希望看到这些功能... :)也很高兴看到水平滚动,并使它看起来像Lollipop上的Material design。
Android开发人员

我已经更新了该项目,使其包括可以与SectionIndexer进行交互的SectionIndicator。查看github了解详细信息。
Daniel Smith

3
观察到这在滚动时并不顺利。它在跳。
2015年

28

新答案:随着时间的流逝,我注意到我的原始答案与其他解决方案相比有一些缺点,特别是对于ViewPager中的片段。

我建议您在不需要冒泡的情况下使用android-x解决方案,或者在需要时使用第三方库(是一个不错的库)。


旧答案:

由于所有第三方库都有问题,因此我决定收集我能找到的东西(大部分是从这里开始),修复所有问题,并发布自己的RecyclerView快速滚动器POC:

https://github.com/AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller

用法:

  1. 制作一个实现BubbleTextGetter的RecyclerView.Adapter,它在数据中给出一个位置将返回要在Bubble-popup中显示的文本。

  2. 将FastScroller放置在包含RecyclerView的布局内(可能在右侧区域)。

  3. 自定义FastScroller FastScroller

一些缺点:

  1. 不支持方向更改,但可能很容易修复。
  2. 不支持其他layoutManagers。只有LinearLayoutManager
  3. 需要API 11及更高版本。

码:

BubbleTextGetter

public interface BubbleTextGetter
  {
  String getTextToShowInBubble(int pos);
  }

recycler_view_fast_scroller__fast_scroller.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       android:layout_width="wrap_content"
       android:layout_height="match_parent">

  <TextView
    android:id="@+id/fastscroller_bubble"
    android:layout_gravity="right|end"
    android:gravity="center"
    android:textSize="48sp" tools:text="A"
    android:layout_width="wrap_content"
    android:textColor="#FFffffff"
    android:layout_height="wrap_content"
    android:background="@drawable/recycler_view_fast_scroller__bubble"
    android:visibility="visible"/>

  <ImageView
    android:id="@+id/fastscroller_handle"
    android:layout_width="wrap_content"
    android:layout_marginRight="8dp"
    android:layout_marginLeft="8dp"
    android:layout_height="wrap_content"
    android:src="@drawable/recycler_view_fast_scroller__handle"/>

</merge>

主要活动

...
fastScroller=(FastScroller)findViewById(R.id.fastscroller);
fastScroller.setRecyclerView(recyclerView);

快速滚动

public class FastScroller extends LinearLayout
  {
  private static final int BUBBLE_ANIMATION_DURATION=100;
  private static final int TRACK_SNAP_RANGE=5;

  private TextView bubble;
  private View handle;
  private RecyclerView recyclerView;
  private final ScrollListener scrollListener=new ScrollListener();
  private int height;

  private ObjectAnimator currentAnimator=null;

  public FastScroller(final Context context,final AttributeSet attrs,final int defStyleAttr)
    {
    super(context,attrs,defStyleAttr);
    initialise(context);
    }

  public FastScroller(final Context context)
    {
    super(context);
    initialise(context);
    }

  public FastScroller(final Context context,final AttributeSet attrs)
    {
    super(context,attrs);
    initialise(context);
    }

  private void initialise(Context context)
    {
    setOrientation(HORIZONTAL);
    setClipChildren(false);
    LayoutInflater inflater=LayoutInflater.from(context);
    inflater.inflate(R.layout.recycler_view_fast_scroller__fast_scroller,this,true);
    bubble=(TextView)findViewById(R.id.fastscroller_bubble);
    handle=findViewById(R.id.fastscroller_handle);
    bubble.setVisibility(INVISIBLE);
    }

  @Override
  protected void onSizeChanged(int w,int h,int oldw,int oldh)
    {
    super.onSizeChanged(w,h,oldw,oldh);
    height=h;
    }

  @Override
  public boolean onTouchEvent(@NonNull MotionEvent event)
    {
    final int action=event.getAction();
    switch(action)
      {
      case MotionEvent.ACTION_DOWN:
        if(event.getX()<handle.getX())
          return false;
        if(currentAnimator!=null)
          currentAnimator.cancel();
        if(bubble.getVisibility()==INVISIBLE)
          showBubble();
        handle.setSelected(true);
      case MotionEvent.ACTION_MOVE:
        setPosition(event.getY());
        setRecyclerViewPosition(event.getY());
        return true;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        handle.setSelected(false);
        hideBubble();
        return true;
      }
    return super.onTouchEvent(event);
    }

  public void setRecyclerView(RecyclerView recyclerView)
    {
    this.recyclerView=recyclerView;
    recyclerView.setOnScrollListener(scrollListener);
    }

  private void setRecyclerViewPosition(float y)
    {
    if(recyclerView!=null)
      {
      int itemCount=recyclerView.getAdapter().getItemCount();
      float proportion;
      if(handle.getY()==0)
        proportion=0f;
      else if(handle.getY()+handle.getHeight()>=height-TRACK_SNAP_RANGE)
        proportion=1f;
      else
        proportion=y/(float)height;
      int targetPos=getValueInRange(0,itemCount-1,(int)(proportion*(float)itemCount));
      recyclerView.scrollToPosition(targetPos);
      String bubbleText=((BubbleTextGetter)recyclerView.getAdapter()).getTextToShowInBubble(targetPos);
      bubble.setText(bubbleText);
      }
    }

  private int getValueInRange(int min,int max,int value)
    {
    int minimum=Math.max(min,value);
    return Math.min(minimum,max);
    }

  private void setPosition(float y)
    {
    int bubbleHeight=bubble.getHeight();
    int handleHeight=handle.getHeight();
    handle.setY(getValueInRange(0,height-handleHeight,(int)(y-handleHeight/2)));
    bubble.setY(getValueInRange(0,height-bubbleHeight-handleHeight/2,(int)(y-bubbleHeight)));
    }

  private void showBubble()
    {
    AnimatorSet animatorSet=new AnimatorSet();
    bubble.setVisibility(VISIBLE);
    if(currentAnimator!=null)
      currentAnimator.cancel();
    currentAnimator=ObjectAnimator.ofFloat(bubble,"alpha",0f,1f).setDuration(BUBBLE_ANIMATION_DURATION);
    currentAnimator.start();
    }

  private void hideBubble()
    {
    if(currentAnimator!=null)
      currentAnimator.cancel();
    currentAnimator=ObjectAnimator.ofFloat(bubble,"alpha",1f,0f).setDuration(BUBBLE_ANIMATION_DURATION);
    currentAnimator.addListener(new AnimatorListenerAdapter()
    {
    @Override
    public void onAnimationEnd(Animator animation)
      {
      super.onAnimationEnd(animation);
      bubble.setVisibility(INVISIBLE);
      currentAnimator=null;
      }

    @Override
    public void onAnimationCancel(Animator animation)
      {
      super.onAnimationCancel(animation);
      bubble.setVisibility(INVISIBLE);
      currentAnimator=null;
      }
    });
    currentAnimator.start();
    }

  private class ScrollListener extends OnScrollListener
    {
    @Override
    public void onScrolled(RecyclerView rv,int dx,int dy)
      {
      View firstVisibleView=recyclerView.getChildAt(0);
      int firstVisiblePosition=recyclerView.getChildPosition(firstVisibleView);
      int visibleRange=recyclerView.getChildCount();
      int lastVisiblePosition=firstVisiblePosition+visibleRange;
      int itemCount=recyclerView.getAdapter().getItemCount();
      int position;
      if(firstVisiblePosition==0)
        position=0;
      else if(lastVisiblePosition==itemCount-1)
        position=itemCount-1;
      else
        position=firstVisiblePosition;
      float proportion=(float)position/(float)itemCount;
      setPosition(height*proportion);
      }
    }
  }

如果您的视图大小不同怎么办?那是我现在的问题。我知道什么观点会改变。但不确定是否可以用它来帮助我。我有几个视图的高度加倍。
乍得宾厄姆

是的,但这有很多帮助。我只想弄清楚如何补偿这些补偿。
乍得宾厄姆

@LoyalRayne请尽管使用Github仓库。在这里可能未更新。
Android开发人员

为什么我的FastScroller不包括所有回收站视图?我遵循了您的所有步骤。.但是显示不正确,请帮助我@androiddeveloper
Michele Lacorte 2015年

1
@MicheleLacorte似乎我忘了添加导入。现在试试。
Android开发人员

11

有很多关于悬而未决的问题RecyclerView,并快速滚动/部分索引,让我们试着重整旗鼓,聚集在这里我们的意见和信息。

简短的答案是: 不,您不能启用快速滚动,因为RecyclerView不包含FastScroller对象,也不任何相关的逻辑状态变量。这是因为RecyclerView不是AbsListView

另一方面,实现RecyclerView包含的转储版本FastScroller和快速滚动所必需的逻辑的a并非不可能,但是到目前为止,我还没有看到任何实现。

请分享您对此的考虑,或者如果您认为我错了。


这是一个好主意。希望我有时间自己尝试并实施。
Android开发人员

1
RecyclerView不能与FastScroller一起使用。它是它的无穷列表的哲学和设计思想。但是没有什么可以阻止一个人尝试的:-)
Sevastyan Savanyuk

1
请删除您的答案,因为这是完全错误的,它将使所有人感到困惑
相信

11

Android支持库26.0.0现在支持 fastScrollEnabled

RecyclerView的新的fastScrollEnabled布尔标志。

如果启用,则必须设置fastScrollHorizo​​ntalThumbDrawable,fastScrollHorizo​​ntalTrackDrawable,fastScrollVerticalThumbDrawable和fastScrollVerticalTrackDrawable。

样本-https: //android.jlelse.eu/fast-scrolling-with-recyclerview-2b89d4574688


2
有趣!那么如何在拖动文本时显示气泡呢?
Android开发人员

1
只需提及这些可绘制对象必须是StateListDrawable的对象,并且还必须为两个轴都设置可绘制对象。
大卫,

1
我刚刚实现了fastScroll使用支持库26.0.1,它仅显示快速滚动条,必须自定义以显示气泡文本
vuhung3990

你说的drawable必须是StateListDrawable是什么意思?
Shrikant '18年

@Shrikant statelist drawable是当您在中实现带有state_pressed="true",带state_selected="true"和只是普通项的项时selector。因此,选择器中将有3个项目。
皮埃尔

4

您也可以将AZ Fastscroll用于RecyclerView。这是iOS风格。

https://github.com/code-computerlove/FastScrollRecyclerView/

如何使用它:

  • 替换android.support.v7.widget.RecyclerViewcom.codecomputerlove.fastscrollrecyclerviewdemo.FastScrollRecyclerView
  • 您的适配器需要实现FastScrollRecyclerViewInterface并重写getMapIndex()。该函数应返回mapIndex。调查calculateIndexesForName()有关如何创建它的灵感。创建后,将其传递给构造函数中的适配器。
  • 创建一个实例FastScrollRecyclerViewItemDecoration并将其添加到您的RecyclerView FastScrollRecyclerViewItemDecoration decoration = new FastScrollRecyclerViewItemDecoration(this); mRecyclerView.addItemDecoration(decoration);
  • 添加<dimen name="fast_scroll_overlay_text_size">100dp</dimen>到您的 /values/dimens.xml文件。这是叠加字母的dp大小

2
这实际上不错,但是它是IOS风格。快速卷轴适用于Android。
android开发人员

4

从Android库26.0.0中为RecyclerView添加了FastScroller功能

编译依赖

    compile 'com.android.support:recyclerview-v7:26.1.0'
    compile 'com.android.support:design:26.1.0'

向project.gradle添加依赖

     maven {
            url "https://maven.google.com"
        }

您的recyclerview.xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    xmlns:tool="http://schemas.android.com/tools"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tool:context=".MainActivity">
    <android.support.v7.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/songlist"
                android:layout_marginStart="8dp"
                android:layout_marginEnd="8dp"
                app:fastScrollEnabled="true"
              app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
                app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
                app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
                app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
               /></LinearLayout>

thumb.xml

   <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <corners
        android:topLeftRadius="44dp"
        android:topRightRadius="44dp"
        android:bottomLeftRadius="44dp"
        android:bottomRightRadius="44dp" />

    <padding
        android:paddingLeft="22dp"
        android:paddingRight="22dp" />

    <solid android:color="#f73831" />

</shape>

line.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <solid android:color="@color/dark_grey" />

    <padding
        android:top="10dp"
        android:left="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
</shape>

thumb_drawable.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/thumb"
        android:state_focused="true"
        android:state_pressed="true" />

    <item android:drawable="@drawable/thumb"
        android:state_focused="false"
        android:state_pressed="true" />
    <item android:drawable="@drawable/thumb" 
            android:state_focused="true" />
    <item android:drawable="@drawable/thumb"
        android:state_focused="false"
        android:state_pressed="false" />
</selector>

line_drawble.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/line"
        android:state_focused="true"
        android:state_pressed="true" />

    <item android:drawable="@drawable/line"
        android:state_focused="false"
        android:state_pressed="true" />
    <item android:drawable="@drawable/line" 
            android:state_focused="true" />
    <item android:drawable="@drawable/line"
        android:state_focused="false"
        android:state_pressed="false" />
</selector>

缺少一些资源:line_dark,line_dark,dark_grey。您在哪里找到此信息?是否有关于它的教程/文章?它是何时添加的(时间)?
Android开发人员

@androiddeveloper,我忘记更改一些元素名称(我和我的代码一样),我没有找到此信息,以上代码是我自己的项目的一部分,我目前正在研究。我认为是很好的共享信息,我可以解决这个问题。
Abhishek Garg '18

那你是怎么找到的呢?而且,请您对其进行更新,以便我可以尝试一下吗?
Android开发人员



3

您可以尝试我们的lib:https//github.com/FutureMind/recycler-fast-scroll。它仍处于早期开发中,但是专门为处理我们在其他库中遇到的平滑性问题而构建。它使用了一些不同的机制。它也支持水平LayoutManager,并且在不久的将来还将支持多列设置。

编辑:现在有一些简洁的自定义选项。


RecyclerView适用于API 7及更高版本。为什么只需要一个简单的视图就需要API 15?此外,它还有一个错误:使用快速滚动条滚动到某个位置,然后正常滚动。滑行者将从当前位置跳下。
android开发人员

原因可能与您的回购中的原因相同。我可以马上将其更改为11,但我将其更改为7时,我有时间来港动画7.谢谢你注意到这个错误,我必须添加水平滚动功能后,推出了它
米哈尔ķ

抱歉。我不记得当时我做了什么。但我没有打算使之在预API 11.工作
Android开发者

这是开箱即用的StaggeredGridLayoutManager的支持者
Daniel F

1

提供了使用RecycleView和来实现滚动条的条款LayoutManager

例如:computeVerticalScrollExtent()computeVerticalScrollOffset()并且computeVerticalScrollRange()可以提供有关始终将垂直滚动拇指定位在正确位置的信息。

这些方法也LayoutManager可以委托实际测量。因此,LayoutManager所使用的实现必须支持这些度量。

此外,在滚动滑块拖动触摸可以通过重写被截获onInterceptTouchEvent()RecyclerView。并且在计算了所需的滚动之后,scrollTo()可以调用update RecyclerView


您能用代码演示一下吗?我问这个问题是为了真正找到一种放置快速滚动器的方法,并且由于我是RecyclerView的新手,所以我很难在短的空闲时间做到这一点
android开发者


0

只需启用快速滚动并为滚动条添加拇指,跟踪器,如下所示。

<android.support.v7.widget.RecyclerView
android:id="@+id/rv_sensors"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable" />

1
是的,在这个事情成为可能之前,有人问过这个问题。我想知道它的可定制性如何。是否可以设置气泡的外观?可能有泡沫吗?如何设置气泡中显示的内容?
Android开发人员

0

android studio中的快速滚动器仍然非常bug,因为它仅基于xml,所以我开始研究可以实现快速滚动的外部库。你可以在这里得到 。易于实现和定制。


看起来不错,但对规则也过于严格。要求我们知道每个项目的高度,并且每个项目类型的高度都相同...
android开发人员
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.