android scrollbar android-recyclerview fastscroll sectionindexer

android - Cómo agregar un desplazamiento rápido a RecyclerView



scrollbar android-recyclerview (9)

Antecedentes

En ListView, podría tener un desplazamiento rápido que le permitiera arrastrar una barra de desplazamiento para desplazarse fácilmente a donde lo desee (utilizando el atributo fastScrollEnabled )

Junto con la clase " SectionIndexer " y opcionalmente algunos atributos, podría tener una buena ventana emergente que se muestra al usar esta barra de desplazamiento (enlace here ).

Tal cosa se muestra en la aplicación de contactos para que pueda desplazarse fácilmente a letras específicas.

El problema

RecyclerView no parece tener ninguno de esos. Ni siquiera un desplazamiento rápido.

La pregunta

¿Cómo agrego una funcionalidad de desplazamiento rápido para RecyclerView?


Como todas las bibliotecas de terceros tenían problemas, he decidido reunir lo que puedo encontrar (principalmente desde here ), arreglar todo y publicar mi propio POC del desplazador rápido de RecyclerView:

https://github.com/AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller

uso:

  1. cree un RecyclerView.Adapter que implemente BubbleTextGetter, que dado una posición en los datos devolverá el texto para mostrar en la burbuja emergente.

  2. coloque el FastScroller dentro del diseño que contiene el RecyclerView (probablemente en el área correcta).

  3. Personaliza el FastScroller FastScroller

Algunas desventajas:

  1. no admite el cambio de orientación, pero probablemente sea fácil de solucionar.
  2. no es compatible con otros layoutManagers. Solo LinearLayoutManager
  3. Necesita API 11 y superior.

Código:

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>

Actividad principal

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


Hay muchas preguntas sin respuesta sobre RecyclerView e indexador de desplazamiento rápido / sección , intentemos reagruparnos y recopilar nuestras opiniones e información aquí.

La respuesta corta es: NO, no puede habilitar el desplazamiento rápido porque RecyclerView no contiene un objeto FastScroller ni ninguna variable de estado lógico relacionada. Esto porque RecyclerView no es AbsListView .

Por otro lado, no es imposible implementar un RecyclerView que contiene una versión FastScroller de FastScroller y la lógica necesaria para el desplazamiento rápido, pero hasta ahora no he visto ninguna implementación de esto.

Comparta su consideración al respecto o si cree que estoy equivocado.


Hay una disposición para implementar barras de desplazamiento con RecycleView y su LayoutManager .

Por ejemplo: computeVerticalScrollExtent() , computeVerticalScrollOffset() y computeVerticalScrollRange() pueden proporcionar información para colocar siempre un pulgar de desplazamiento vertical en el lugar correcto.

Estos métodos también están disponibles en LayoutManager para delegar mediciones reales. Por lo tanto, la implementación de LayoutManager utilizada debe admitir estas mediciones.

Además, el toque de arrastre en el pulgar de desplazamiento se puede interceptar anulando onInterceptTouchEvent() de RecyclerView . Y después de calcular el desplazamiento deseado, se puede llamar a scrollTo() para actualizar RecyclerView .


La biblioteca de soporte de Android 26.0.0 ahora es compatible con fastScrollEnabled

Nuevo indicador booleano fastScrollEnabled para RecyclerView.

Si está habilitado, se deben configurar fastScrollHorizontalThumbDrawable, fastScrollHorizontalTrackDrawable, fastScrollVerticalThumbDrawable y fastScrollVerticalTrackDrawable.

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


La funcionalidad FastScroller se agrega desde la biblioteca de Android 26.0.0 para RecyclerView

compilar dependencia

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

agregue dependencia al project.gradle

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

su archivo 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>


Me encontré con esta pregunta hace unos días cuando me encontré con esta situación. Aquí está mi ejemplo de implementación de FastScroll para RecyclerView :

github.com/danoz73/RecyclerViewFastScroller

Intente ejecutar la aplicación de ejemplo y lea el código para ver un uso bastante simple de un widget RecyclerViewFastScroller simple. Hay información sobre github, pero incluiré el uso básico aquí para un desplazamiento rápido vertical.

Para un ejemplo completo, consulte la aplicación de muestra en el repositorio .

Uso básico

En la actividad o fragmento XML donde reside su RecyclerView, incluya un objeto VerticalRecyclerViewFastScroller. El siguiente ejemplo estaría en un diseño relativo:

... <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" /> ...

En su fragmento o actividad donde configura el diseño mediante programación, conecte el desplazador rápido al reciclador:

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

¡Espero que esto ayude!

EDITAR : ¡Ahora se agregó soporte para los indicadores de sección de estilo Android-Lollipop-Contacts! Consulte la implementación de la aplicación de muestra para obtener más detalles.


Puede probar nuestra biblioteca: https://github.com/FutureMind/recycler-fast-scroll . Todavía está en desarrollo temprano, pero fue construido específicamente para lidiar con el problema de suavidad que experimentamos con otras bibliotecas. Utiliza un mecanismo un poco diferente. También es compatible con LayoutManager horizontal y también admitirá configuraciones de varias columnas en un futuro próximo.

Editar: hay algunas opciones de personalización ordenadas ahora.


Simplemente habilite el desplazamiento rápido y agregue el pulgar, el rastreador para la barra de desplazamiento como a continuación.

<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" />


También puede usar AZ Fastscroll para RecyclerView. Es el estilo iOS.

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

Cómo usarlo:

  • Reemplace android.support.v7.widget.RecyclerView con com.codecomputerlove.fastscrollrecyclerviewdemo.FastScrollRecyclerView
  • Su adaptador necesita implementar FastScrollRecyclerViewInterface y anular getMapIndex() . La función debe devolver el mapIndex. Busque en calculateIndexesForName() para obtener inspiración sobre cómo crearlo. Una vez creado, pásalo al adaptador en el constructor.
  • Cree una instancia de FastScrollRecyclerViewItemDecoration y agréguela a su RecyclerView FastScrollRecyclerViewItemDecoration decoration = new FastScrollRecyclerViewItemDecoration(this); mRecyclerView.addItemDecoration(decoration); FastScrollRecyclerViewItemDecoration decoration = new FastScrollRecyclerViewItemDecoration(this); mRecyclerView.addItemDecoration(decoration);
  • agregue <dimen name="fast_scroll_overlay_text_size">100dp</dimen> a su archivo /values/dimens.xml . Este es el tamaño dp de la letra superpuesta