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:
-
cree un RecyclerView.Adapter que implemente BubbleTextGetter, que dado una posición en los datos devolverá el texto para mostrar en la burbuja emergente.
-
coloque el FastScroller dentro del diseño que contiene el RecyclerView (probablemente en el área correcta).
-
Personaliza el FastScroller FastScroller
Algunas desventajas:
- no admite el cambio de orientación, pero probablemente sea fácil de solucionar.
- no es compatible con otros layoutManagers. Solo LinearLayoutManager
- 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
concom.codecomputerlove.fastscrollrecyclerviewdemo.FastScrollRecyclerView
-
Su adaptador necesita implementar FastScrollRecyclerViewInterface y anular
getMapIndex()
. La función debe devolver el mapIndex. Busque encalculateIndexesForName()
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 RecyclerViewFastScrollRecyclerViewItemDecoration 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