如何添加一个快速滚动到RecyclerView

背景

在ListView上,你可以有一个快速的滚动条,它允许你拖动一个滚动条,以方便地滚动到你想要的地方(使用fastScrollEnabled属性)

与“ SectionIndexer ”类以及可选的一些属性一起,您可以在使用此滚动条时显示一个漂亮的popup窗口(链接在这里 )。

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

问题

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

这个问题

如何为RecyclerView添加快速滚动function?

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

github.com/danoz73/RecyclerViewFastScroller

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

有关完整的示例,请参阅回购示例中的示例应用程序 。

基本用法

在您的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的棒棒糖联系人式部分指标的支持! 查看示例应用程序的实现细节。

由于所有第三方库都有问题,我决定收集我能find的东西(大部分来自这里 ),修复所有问题并发布我自己的RecyclerView快速卷轴的POC:

https://github.com/AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller

用法:

  1. 创build一个RecyclerView.Adapter实现BubbleTextGetter,它给定数据中的一个位置将返回文本显示在气泡popup。

  2. 将FastScroller放置在容器RecyclerView的布局中(可能位于正确的区域)。

  3. 自定义FastScroller FastScroller

一些缺点:

  1. 不支持定位更改,但可能很容易修复。
  2. 不支持其他布局pipe理器。 只有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); 

FastScroller

 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); } } } 

关于RecyclerView快速滚动/分段索引器有很多没有答案的问题,让我们尝试在这里重新组织和收集我们的意见和信息。

简短的回答是: 否,您不能启用快速滚动,因为RecyclerView不包含FastScroller对象,也没有任何相关的逻辑状态variables。 这是因为RecyclerView不是一个AbsListView 。

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

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

Android支持库26.0.0现在支持fastScrollEnabled

RecyclerView新的fastScrollEnabled布尔标志。

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

示例 – https://android.jlelse.eu/fast-scrolling-with-recyclerview-2b89d4574688

您也可以使用AZ快速滚动查看RecyclerView。 这是iOS风格。

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

如何使用它:

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

你可以试试我们的lib: https : //github.com/FutureMind/recycler-fast-scroll 。 它还处于早期的发展阶段,但是专门用来处理我们与其他图书馆经历的平滑问题。 它使用一点点不同的机制。 它还支持水平的LayoutManager,并且在不久的将来也将支持多列设置。

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

有一个使用RecycleView及其LayoutManager实现滚动条的规定。

例如: computeVerticalScrollExtent()computeVerticalScrollOffset()computeVerticalScrollRange()可以提供将垂直滚动条放置在正确位置的信息。

这些方法也在LayoutManager用于委托实际测量。 所以使用的LayoutManager实现必须支持这些测量。

另外,可以通过覆盖onInterceptTouchEvent()来拦截滚动缩略图上的拖动触摸。 在计算完所需的滚动条后,可以调用scrollTo()来更新RecyclerView