如何将快速滚动器添加到 RecyclerView
How to add a fast-scroller to the RecyclerView
背景
在 ListView 上,您可以有一个快速滚动条,它允许您拖动滚动条以轻松滚动到您想要的任何位置(使用 fastScrollEnabled 属性)
连同“SectionIndexer" class and optionally some attributes, you could have a nice popup that shows as you use this scrollbar (link here).
这样的东西会显示在联系人应用程序上,以便您可以轻松滚动到特定的字母。
问题
RecyclerView 似乎没有这些。甚至没有快速滚动。
问题
如何为 RecyclerView 添加快速滚动条功能?
关于 RecyclerView
和 fast-scroll/section 索引器 有很多未解决的问题,让我们尝试重新组合并在这里收集我们的意见和信息。
简短的回答是: 不,你不能启用快速滚动因为RecyclerView does not contain a FastScroller object and nor any related logical-state variables. This because RecyclerView
is not an AbsListView.
另一方面,实现包含 FastScroller
的转储版本和快速滚动的必要逻辑的 RecyclerView
并非不可能,但我还没有看到任何实现到目前为止。
请分享您对此的看法,或者您是否认为我错了。
提供了使用 RecycleView
及其 LayoutManager
实现滚动条的规定。
例如:computeVerticalScrollExtent()
, computeVerticalScrollOffset()
and computeVerticalScrollRange()
可以提供始终将垂直滚动滑块定位在正确位置的信息。
这些方法也存在于 LayoutManager
中,用于委托实际测量。因此,所使用的 LayoutManager
实现必须支持这些测量。
此外,滚动拇指上的拖动触摸可以通过覆盖 onInterceptTouchEvent()
of RecyclerView
. And after calculating the desired scroll, scrollTo()
来拦截,可以调用以更新 RecyclerView
。
几天前我 运行 遇到这种情况时偶然发现了这个问题。这是我的 RecyclerView 的 FastScroll 示例实现:
github.com/danoz73/RecyclerViewFastScroller
尝试 运行 示例应用程序,并仔细阅读代码以了解简单 RecyclerViewFastScroller 小部件的相当简单的用法。有关于 github 的信息,但我将在此处包括垂直快速滚动条的基本用法。
有关完整示例,请参阅 sample application in the repo。
基本用法
在您的 RecyclerView 所在的 activity 或片段 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"
/>
...
在您的片段或以编程方式设置布局的 activity 中,将快速滚动条连接到回收器:
...
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-style section indicators 的支持!查看 sample application's implementation for details.
您也可以为 RecyclerView 使用 A-Z Fastscroll。这是 iOS 风格。
https://github.com/code-computerlove/FastScrollRecyclerView/
使用方法:
- 将
android.support.v7.widget.RecyclerView
替换为com.codecomputerlove.fastscrollrecyclerviewdemo.FastScrollRecyclerView
- 您的适配器需要实现 FastScrollRecyclerViewInterface 并覆盖
getMapIndex()
。该函数应该 return 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大小
新答案:
随着时间的推移,我注意到我的原始答案与其他解决方案相比有一些缺点,尤其是对于 ViewPager 中的片段。
我建议要么使用 in case you don't need a bubble, or a third party library (here 是个不错的选择)以防万一。
旧答案:
由于所有第三方库都有问题,我决定收集我能找到的东西(主要来自 here),修复所有内容并发布我自己的 RecyclerView 快速滚动器的 POC:
https://github.com/AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller
用法:
制作一个 RecyclerView.Adapter 实现 BubbleTextGetter,给定数据中的位置将 return 显示在气泡弹出窗口中的文本。
将 FastScroller 放置在包含 RecyclerView 的布局中(可能在正确的区域)。
自定义 FastScroller FastScroller
一些缺点:
- 不支持方向更改,但可能很容易修复。
- 不支持其他布局管理器。只有 LinearLayoutManager
- 需要 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>
MainActivity
...
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);
}
}
}
您可以试试我们的库:https://github.com/FutureMind/recycler-fast-scroll。它仍处于早期开发阶段,但专门用于处理我们在其他库中遇到的流畅性问题。它使用了一些不同的机制。它还支持水平 LayoutManager,并且在不久的将来还将支持多列设置。
编辑:现在有一些简洁的自定义选项。
Android 支持库 26.0.0 现在支持 fastScrollEnabled
RecyclerView 的新 fastScrollEnabled 布尔标志。
如果启用,则必须设置 fastScrollHorizontalThumbDrawable、fastScrollHorizontalTrackDrawable、fastScrollVerticalThumbDrawable 和 fastScrollVerticalTrackDrawable。
示例 - https://android.jlelse.eu/fast-scrolling-with-recyclerview-2b89d4574688
FastScroller 功能已从 android 库 26.0.0 添加到 RecyclerView
编译依赖
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>
只需启用快速滚动并添加拇指、滚动条跟踪器,如下所示。
<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" />
这个新库基于框架启动器的快速滚动器:https://github.com/zhanghai/AndroidFastScroll
new FastScrollerBuilder(recyclerView).build();
可选择在适配器中实现 PopupTextProvider
。
android studio 中的快速滚动器仍然有很多错误,因为它仅基于 xml,所以我开始挖掘可以实现快速滚动的外部库。你可以的
here。非常容易实施和定制。
背景
在 ListView 上,您可以有一个快速滚动条,它允许您拖动滚动条以轻松滚动到您想要的任何位置(使用 fastScrollEnabled 属性)
连同“SectionIndexer" class and optionally some attributes, you could have a nice popup that shows as you use this scrollbar (link here).
这样的东西会显示在联系人应用程序上,以便您可以轻松滚动到特定的字母。
问题
RecyclerView 似乎没有这些。甚至没有快速滚动。
问题
如何为 RecyclerView 添加快速滚动条功能?
关于 RecyclerView
和 fast-scroll/section 索引器 有很多未解决的问题,让我们尝试重新组合并在这里收集我们的意见和信息。
简短的回答是: 不,你不能启用快速滚动因为RecyclerView does not contain a FastScroller object and nor any related logical-state variables. This because RecyclerView
is not an AbsListView.
另一方面,实现包含 FastScroller
的转储版本和快速滚动的必要逻辑的 RecyclerView
并非不可能,但我还没有看到任何实现到目前为止。
请分享您对此的看法,或者您是否认为我错了。
提供了使用 RecycleView
及其 LayoutManager
实现滚动条的规定。
例如:computeVerticalScrollExtent()
, computeVerticalScrollOffset()
and computeVerticalScrollRange()
可以提供始终将垂直滚动滑块定位在正确位置的信息。
这些方法也存在于 LayoutManager
中,用于委托实际测量。因此,所使用的 LayoutManager
实现必须支持这些测量。
此外,滚动拇指上的拖动触摸可以通过覆盖 onInterceptTouchEvent()
of RecyclerView
. And after calculating the desired scroll, scrollTo()
来拦截,可以调用以更新 RecyclerView
。
几天前我 运行 遇到这种情况时偶然发现了这个问题。这是我的 RecyclerView 的 FastScroll 示例实现:
github.com/danoz73/RecyclerViewFastScroller
尝试 运行 示例应用程序,并仔细阅读代码以了解简单 RecyclerViewFastScroller 小部件的相当简单的用法。有关于 github 的信息,但我将在此处包括垂直快速滚动条的基本用法。
有关完整示例,请参阅 sample application in the repo。
基本用法
在您的 RecyclerView 所在的 activity 或片段 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"
/>
...
在您的片段或以编程方式设置布局的 activity 中,将快速滚动条连接到回收器:
...
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-style section indicators 的支持!查看 sample application's implementation for details.
您也可以为 RecyclerView 使用 A-Z Fastscroll。这是 iOS 风格。
https://github.com/code-computerlove/FastScrollRecyclerView/
使用方法:
- 将
android.support.v7.widget.RecyclerView
替换为com.codecomputerlove.fastscrollrecyclerviewdemo.FastScrollRecyclerView
- 您的适配器需要实现 FastScrollRecyclerViewInterface 并覆盖
getMapIndex()
。该函数应该 return mapIndex。查看calculateIndexesForName()
以获取有关如何创建它的灵感。创建后将其传递给构造函数中的适配器。 - 创建
FastScrollRecyclerViewItemDecoration
的实例并将其添加到您的 RecyclerViewFastScrollRecyclerViewItemDecoration decoration = new FastScrollRecyclerViewItemDecoration(this); mRecyclerView.addItemDecoration(decoration);
- 将
<dimen name="fast_scroll_overlay_text_size">100dp</dimen>
添加到您的/values/dimens.xml
文件。这是叠加字母的dp大小
新答案: 随着时间的推移,我注意到我的原始答案与其他解决方案相比有一些缺点,尤其是对于 ViewPager 中的片段。
我建议要么使用
旧答案:
由于所有第三方库都有问题,我决定收集我能找到的东西(主要来自 here),修复所有内容并发布我自己的 RecyclerView 快速滚动器的 POC:
https://github.com/AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller
用法:
制作一个 RecyclerView.Adapter 实现 BubbleTextGetter,给定数据中的位置将 return 显示在气泡弹出窗口中的文本。
将 FastScroller 放置在包含 RecyclerView 的布局中(可能在正确的区域)。
自定义 FastScroller FastScroller
一些缺点:
- 不支持方向更改,但可能很容易修复。
- 不支持其他布局管理器。只有 LinearLayoutManager
- 需要 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>
MainActivity
...
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);
}
}
}
您可以试试我们的库:https://github.com/FutureMind/recycler-fast-scroll。它仍处于早期开发阶段,但专门用于处理我们在其他库中遇到的流畅性问题。它使用了一些不同的机制。它还支持水平 LayoutManager,并且在不久的将来还将支持多列设置。
编辑:现在有一些简洁的自定义选项。
Android 支持库 26.0.0 现在支持 fastScrollEnabled
RecyclerView 的新 fastScrollEnabled 布尔标志。
如果启用,则必须设置 fastScrollHorizontalThumbDrawable、fastScrollHorizontalTrackDrawable、fastScrollVerticalThumbDrawable 和 fastScrollVerticalTrackDrawable。
示例 - https://android.jlelse.eu/fast-scrolling-with-recyclerview-2b89d4574688
FastScroller 功能已从 android 库 26.0.0 添加到 RecyclerView
编译依赖
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>
只需启用快速滚动并添加拇指、滚动条跟踪器,如下所示。
<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" />
这个新库基于框架启动器的快速滚动器:https://github.com/zhanghai/AndroidFastScroll
new FastScrollerBuilder(recyclerView).build();
可选择在适配器中实现 PopupTextProvider
。
android studio 中的快速滚动器仍然有很多错误,因为它仅基于 xml,所以我开始挖掘可以实现快速滚动的外部库。你可以的 here。非常容易实施和定制。