Как добавить быстрый скроллер в RecyclerView
Фон
В ListView у вас может быть быстрая прокрутка, которая позволяет перетаскивать полосу прокрутки, чтобы легко прокручивать туда, куда вы хотите (используя атрибут fastScrollEnabled)
Вместе с классом " SectionIndexer " и, возможно, некоторыми атрибутами, у вас может появиться приятное всплывающее окно, которое отображается при использовании этой полосы прокрутки (ссылка здесь).
Такая вещь отображается в приложении контактов, чтобы вы могли легко прокручивать до конкретных букв.
Эта проблема
RecyclerView, похоже, не имеет ни одного из них. Даже не быстрая прокрутка.
Вопрос
Как добавить функциональность быстрой прокрутки для RecyclerView?
9 ответов
Поскольку у всех сторонних библиотек были проблемы, я решил собрать то, что я могу найти (в основном отсюда), исправить все и опубликовать свой собственный POC быстрой прокрутки RecyclerView:
https://github.com/AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller
использование:
создайте RecyclerView.Adapter, который реализует BubbleTextGetter, который с учетом позиции в данных вернет текст, отображаемый в всплывающем окне.
Поместите FastScroller внутри макета, который содержит RecyclerView (возможно, в правой области).
Настройка FastScroller FastScroller
Некоторые недостатки:
- не поддерживает изменение ориентации, но, вероятно, это легко исправить.
- не поддерживает других layoutManager. Только 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>
Основная деятельность
...
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);
}
}
}
Я наткнулся на этот вопрос несколько дней назад, когда столкнулся с этой ситуацией. Вот мой пример реализации FastScroll для RecyclerView:
https://github.com/danoz73/RecyclerViewFastScroller
Попробуйте запустить пример приложения и просмотреть код, чтобы увидеть довольно простое использование простого виджета RecyclerViewFastScroller. Есть информация о github, но я включу здесь основное использование вертикальной быстрой прокрутки.
Полный пример приведен в примере приложения в репо.
Основное использование
В XML-код активности или фрагмента, в котором находится RecyclerView, включите объект 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! Проверьте реализацию примера приложения для деталей.
Библиотека поддержки Android 26.0.0 теперь поддерживает fastScrollEnabled
Новый логический флаг fastScrollEnabled для RecyclerView.
Если включено, должны быть установлены fastScrollHor horizontalThumbDrawable, fastScrollHor horizontalTrackDrawable, fastScrollVerticalThumbDrawable и fastScrollVerticalTrackDrawable.
Образец - https://android.jlelse.eu/fast-scrolling-with-recyclerview-2b89d4574688
Есть много вопросов без ответа о RecyclerView
и быстрая прокрутка / индексатор разделов, давайте попробуем перегруппировать и собрать наши мнения и информацию здесь.
Краткий ответ: НЕТ, вы не можете включить быструю прокрутку, потому что RecyclerView не содержит ни объекта FastScroller, ни связанных с ним переменных логического состояния. Это потому что RecyclerView
не является AbsListView.
С другой стороны, это не невозможно реализовать RecyclerView
который содержит дамп версию FastScroller
и необходимая логика для быстрой прокрутки, но я не видел никакой реализации этого до сих пор.
Пожалуйста, поделитесь своим мнением об этом, или если вы думаете, что я неправ.
Функциональность 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>
Вы также можете использовать AZ Fastscroll для RecyclerView. Это стиль iOS.
https://github.com/code-computerlove/FastScrollRecyclerView/
Как это использовать:
- замещать
android.support.v7.widget.RecyclerView
сcom.codecomputerlove.fastscrollrecyclerviewdemo.FastScrollRecyclerView
- Ваш адаптер должен реализовать FastScrollRecyclerViewInterface и переопределить
getMapIndex()
, Функция должна возвращать mapIndex. Заглянуть вcalculateIndexesForName()
для вдохновения о том, как его создать. После создания передайте его адаптеру в конструкторе. - Создать экземпляр
FastScrollRecyclerViewItemDecoration
и добавьте его в свой RecyclerViewFastScrollRecyclerViewItemDecoration decoration = new FastScrollRecyclerViewItemDecoration(this); mRecyclerView.addItemDecoration(decoration);
- добавлять
<dimen name="fast_scroll_overlay_text_size">100dp</dimen>
на ваш/values/dimens.xml
файл. Это размер dp наложенной буквы
Вы можете попробовать нашу библиотеку: https://github.com/FutureMind/recycler-fast-scroll. Он все еще находится на ранней стадии разработки, но был создан специально для решения проблемы гладкости, с которой мы столкнулись в других библиотеках. Он использует немного другой механизм. Он также поддерживает горизонтальный LayoutManager и в ближайшем будущем также будет поддерживать настройки нескольких столбцов.
Изменить: теперь есть несколько опций настройки.
Эта новая библиотека была основана на быстром скроллере средства запуска фреймворка: https://github.com/zhanghai/AndroidFastScroll
new FastScrollerBuilder(recyclerView).build();
При желании реализовать PopupTextProvider
в адаптере.
Существует возможность реализации полос прокрутки с RecycleView
И его LayoutManager
,
Например: computeVerticalScrollExtent()
, computeVerticalScrollOffset()
а также computeVerticalScrollRange()
может предоставить информацию для того, чтобы всегда располагать вертикальный большой палец в нужном месте.
Эти методы также есть в LayoutManager
для делегирования фактических измерений. Итак LayoutManager
используемая реализация должна поддерживать эти измерения.
Кроме того, перетаскивание на большой палец прокрутки может быть перехвачено путем переопределения onInterceptTouchEvent()
из RecyclerView
, И после расчета желаемого прокрутки, scrollTo()
можно вызвать для обновления RecyclerView
,
Быстрая прокрутка в студии Android по-прежнему очень глючна, поскольку основана только на xml, поэтому я стал искать внешние библиотеки, которые могли бы реализовать быструю прокрутку. Вы можете получить это здесь. Очень легко реализовать и настроить.
Просто включите быструю прокрутку и добавьте большой палец, трекер для полосы прокрутки, как показано ниже.
<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" />