setnestedscrollingenabled recyclerview inside fast ejemplo bottom android scroll android-recyclerview

android - recyclerview - Obtener el elemento visible del centro de RecycleView cuando se desplaza



recyclerview scroll to bottom (10)

A veces se necesita todo el bloque de código de ejemplo juntos, porque podemos perder algo. Esto es lo que tengo, siéntete libre de corregir cualquier cosa, ya que puedo estar cometiendo un pequeño error en alguna parte. Y sí, esta respuesta es una extensión de @tranhieu answer. Gracias @tranhieu.

MainActivity.java

package com.test; import android.app.Activity; import android.graphics.Color; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.TextView; import java.util.ArrayList; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); public float firstItemWidthDate; public float paddingDate; public float itemWidthDate; public int allPixelsDate; public int finalWidthDate; private DateAdapter dateAdapter; private ArrayList<LabelerDate> labelerDates = new ArrayList<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getRecyclerviewDate(); } public void getRecyclerviewDate() { final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date); if (recyclerViewDate != null) { recyclerViewDate.postDelayed(new Runnable() { @Override public void run() { setDateValue(); } }, 300); recyclerViewDate.postDelayed(new Runnable() { @Override public void run() { recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1); setDateValue(); } }, 5000); } ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver(); vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this); finalWidthDate = recyclerViewDate.getMeasuredWidth(); itemWidthDate = getResources().getDimension(R.dimen.item_dob_width); paddingDate = (finalWidthDate - itemWidthDate) / 2; firstItemWidthDate = paddingDate; allPixelsDate = 0; final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext()); dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerViewDate.setLayoutManager(dateLayoutManager); recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); synchronized (this) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { calculatePositionAndScrollDate(recyclerView); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); allPixelsDate += dx; } }); if (labelerDates == null) { labelerDates = new ArrayList<>(); } genLabelerDate(); dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate); recyclerViewDate.setAdapter(dateAdapter); dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1); return true; } }); } private void genLabelerDate() { for (int i = 0; i < 32; i++) { LabelerDate labelerDate = new LabelerDate(); labelerDate.setNumber(Integer.toString(i)); labelerDates.add(labelerDate); if (i == 0 || i == 31) { labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING); } else { labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM); } } } /* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/ private void calculatePositionAndScrollDate(RecyclerView recyclerView) { int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate); if (expectedPositionDate == -1) { expectedPositionDate = 0; } else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) { expectedPositionDate--; } scrollListToPositionDate(recyclerView, expectedPositionDate); } /* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/ private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) { float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate; float missingPxDate = targetScrollPosDate - allPixelsDate; if (missingPxDate != 0) { recyclerView.smoothScrollBy((int) missingPxDate, 0); } setDateValue(); } // private void setDateValue() { int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate); int setColorDate = expectedPositionDateColor + 1; // set color here dateAdapter.setSelecteditem(setColorDate); } public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> { private ArrayList<LabelerDate> dateDataList; private static final int VIEW_TYPE_PADDING = 1; private static final int VIEW_TYPE_ITEM = 2; private int paddingWidthDate = 0; private int selectedItem = -1; public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) { this.dateDataList = dateData; this.paddingWidthDate = paddingWidthDate; } @Override public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_ITEM) { final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); return new DateViewHolder(view); } else { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams(); layoutParams.width = paddingWidthDate; view.setLayoutParams(layoutParams); return new DateViewHolder(view); } } @Override public void onBindViewHolder(DateViewHolder holder, int position) { LabelerDate labelerDate = dateDataList.get(position); if (getItemViewType(position) == VIEW_TYPE_ITEM) { holder.tvDate.setText(labelerDate.getNumber()); holder.tvDate.setVisibility(View.VISIBLE); Log.d(TAG, "default " + position + ", selected " + selectedItem); if (position == selectedItem) { Log.d(TAG, "center" + position); holder.tvDate.setTextColor(Color.parseColor("#76FF03")); holder.tvDate.setTextSize(35); } else { holder.tvDate.setTextColor(Color.WHITE); holder.tvDate.setTextSize(18); } } else { holder.tvDate.setVisibility(View.INVISIBLE); } } public void setSelecteditem(int selecteditem) { this.selectedItem = selecteditem; notifyDataSetChanged(); } @Override public int getItemCount() { return dateDataList.size(); } @Override public int getItemViewType(int position) { LabelerDate labelerDate = dateDataList.get(position); if (labelerDate.getType() == VIEW_TYPE_PADDING) { return VIEW_TYPE_PADDING; } else { return VIEW_TYPE_ITEM; } } public class DateViewHolder extends RecyclerView.ViewHolder { public TextView tvDate; public DateViewHolder(View itemView) { super(itemView); tvDate = (TextView) itemView.findViewById(R.id.txt_date); } } } private class LabelerDate { private int type; private String number; public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public int getType() { return type; } public void setType(int type) { this.type = type; } } }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_tasks_date" android:layout_width="match_parent" android:layout_height="48dp" /> <ImageView android:layout_width="48dp" android:layout_height="48dp" android:layout_gravity="center" android:layout_marginTop="48dp" android:src="@android:drawable/ic_dialog_info" /> </FrameLayout> </LinearLayout>

item.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent"> <TextView android:id="@+id/txt_date" android:layout_width="@dimen/item_dob_width" android:layout_height="48dp" android:text="32" android:textColor="@android:color/white" android:background="@android:color/darker_gray" android:textSize="28sp" android:gravity="center"/> </LinearLayout>

dimens.xml

<resources> <dimen name="item_dob_width">100dp</dimen> </resources>

Esto es lo que quiero:

Como imagen de arriba, quiero dibujar una línea central en RecycleView , luego obtener el elemento central cuando se desplaza (así como mover hacia la izquierda o hacia la derecha)
Aquí está mi intento de dibujar un RecycleView horizontal:

HorizontalAdapter adapter = new HorizontalAdapter(data); LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); recycleView.setLayoutManager(layoutManager); recycleView.setAdapter(adapter);

¿Hay alguna forma de saber qué elemento se mueve al centro de RecycleView ? ¿Y cómo puedo desplazar RecycleView hacia la izquierda o hacia la derecha en una sola posición?

Actualización : Intenté usar un detector de desplazamiento para obtener la posición media, pero no funciona como aspecto.

@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int firstPos = layoutManager.findFirstVisibleItemPosition(); int lastPos = layoutManager.findLastVisibleItemPosition(); int middle = Math.abs(lastPos - firstPos) / 2 + firstPos; int selectedPos = -1; for (int i = 0; i < adapter.getItemCount(); i++) { if (i == middle) { adapter.getItem(i).setSelected(true); selectedPos = i; } else { adapter.getItem(i).setSelected(false); } } adapter.notifyDataSetChanged(); }

Y obtiene el resultado:

Solo quiero cambiar el elemento seleccionado (hacer texto en color blanco) cuando está en el azul Rect


Al principio, necesitaba algo similar, no esto. Pero pude adaptar la solución @TranHieu a mis necesidades, así que voté por su solución.

Quería crear una vista de reciclador horizontal de pantalla completa que, después de que el usuario actualice, actualice scrollPosition a mostVisibleItem.

preparar:

private void setUpScrolling() { mRecyclerVIew.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { mRecyclerVIew.getViewTreeObserver().removeOnPreDrawListener(this); CustomScrollListener listener = (CustomScrollListener) mScrollListener; listener.width = mRecyclerVIew.getMeasuredWidth(); listener.dx = 0; return true; } }); }

oyente:

private class CustomScrollListener extends OnScrollListener { private int mLastDx = 0; int width = 0; int dx = 0; @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (mLastDx != dx) { scrollToMostVisibleItem(); } else { dx = 0; mLastDx = 0; } } super.onScrollStateChanged(recyclerView, newState); } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); this.dx += dx; } private void scrollToMostVisibleItem() { int direction = (dx > 0) ? 1 : -1; dx = Math.abs(dx); int shiftCount = Math.round(dx / width); int pixelShift = dx % width; if (pixelShift > width / 2) { shiftCount++; } float targetScrollPixels = shiftCount * width; float finalScrollPixels = (targetScrollPixels - dx) * direction; if (finalScrollPixels != 0) { mRecyclerVIew.smoothScrollBy((int) finalScrollPixels, 0); mLastDx = (int) finalScrollPixels; dx = 0; } } }


Como se menciona en la otra respuesta, no hay una forma directa de hacer esto.

Esta es probablemente la forma en que puede lograr lo que describió en la pregunta.

  1. Sepa la cantidad de elementos visibles en la pantalla.
  2. Seleccione el artículo medio programáticamente cada vez que se desplaza la vista.
  3. Mantenga una imagen parcialmente transparente como una superposición en el elemento del medio en la vista del reciclador. (Necesitará calcular las coordenadas según el ancho de la vista del reciclador o el ancho de la pantalla y el ancho de la imagen superpuesta que elija colocar.
  4. Actualice el valor seleccionado en una vista de texto debajo de la vista de reciclador cada vez que haya un desplazamiento.

Las superposiciones de imágenes deben colocarse de forma que parezcan conectadas y como un solo control.


Estoy usando el SnapHelper aquí mismo:

// init snaphelper SnapHelper snapHelper = new LinearSnapHelper(); snapHelper.attachToRecyclerView(recyclerView) // init layout manager LinearLayoutManager layoutManager = new LinearLayoutManager(mainActivity); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerView.setLayoutManager(layoutManager); // init adapter adatper.setSnapHelper(snapHelper); adatper.setLayoutManager(layoutManager); adatper.initAdapter(new Float((DisplayHelper.getDisplayWidth(mainActivity) / 2) - (fooViewWidth / 2)).intValue()); recyclerView.setAdapter(adatper);

Como dijo TranHieu, la solución de insertar 2 elementos para el relleno (en las posiciones inicial y final) es buena.

No me gusta el uso de ViewTreeObserver debido a la mala legibilidad del código. Con esta técnica, también debe gestionar el redibujado de los elementos si se reciclan.

Si usa clases de vista personalizada, puede establecer su ancho directamente en estas clases.

Por ejemplo, esta es mi clase de relleno

/** * Created by firegloves on 25/09/15. */ @EViewGroup(R.layout.view_padding) public class PaddingView extends FooView { Context mCtx; public PaddingView(Context context) { super(context); mCtx = context; } public void setWidth(int width) { setLayoutParams(new LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT)); } }

En mi adaptador, almaceno el ancho del elemento de relleno deseado, que es igual a (displayWidth / 2) - (realItemWidth / 2)

Este es mi adaptador, no mire métodos que no coincidan con RecyclerView.Adapter, preste atención al método initAdapter y al método onCreateItemView

@EBean public class FooAdapterRecycler extends RecyclerViewAdapterBase<Foo, FooView> { private final int TYPE_PADDING_VIEW = 0; private final int TYPE_REAL_VIEW = 1; @RootContext Context ctx; @Bean(Finder.class) IFinder finder; SnapHelper snapHelper; RecyclerView.LayoutManager layoutManager; private int paddingWidth = 0; /** * preleva i dati dal finder */ public void initAdapter(int paddingWidth) { /******************************* * THIS CODE IS THE IMPORTANT ONE ******************************/ this.paddingWidth = paddingWidth; // add 1 item for initial space mItems = new ArrayList<>(); Foo foo = new Foo(); mItems.add(foo); // get real items from finder mItems.addAll(finder.findAll()); // add 1 item for final space mItems = new ArrayList<>(); Foo foo2 = new Foo(); mItems.add(foo2); } @Override public int getItemViewType(int position) { if (position == 0 || position == getItemCount()-1) { return TYPE_PADDING_VIEW; } else { return TYPE_REAL_VIEW; } } @Override protected FooView onCreateItemView(ViewGroup parent, int viewType) { /******************************* * THIS CODE IS THE IMPORTANT ONE ******************************/ if (viewType == TYPE_PADDING_VIEW) { PaddingView view = PaddingView_.build(ctx); view.setWidth(paddingWidth); return view; } else { return FooView_.build(ctx); } } public void setSnapHelper(SnapHelper snapHelper) { this.snapHelper = snapHelper; } public void setLayoutManager(RecyclerView.LayoutManager layoutManager) { this.layoutManager = layoutManager; } }

Estoy usando la biblioteca de Android Anotaciones, pero no es obligatorio

Espero que ayude


Hice algo así como esto. Puedo hacer exactamente lo que necesitas. Antes que nada, así es como funciona mi alogrithm

Este es mi adaptador recyclerView

public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> { private ArrayList<LabelerDate> dateDataList; private static final int VIEW_TYPE_PADDING = 1; private static final int VIEW_TYPE_ITEM = 2; private int paddingWidthDate = 0; private int selectedItem = -1; public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) { this.dateDataList = dateData; this.paddingWidthDate = paddingWidthDate; } @Override public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_ITEM) { final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_date, parent, false); return new DateViewHolder(view); } else { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_padding, parent, false); RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams(); layoutParams.width = paddingWidthDate; view.setLayoutParams(layoutParams); return new DateViewHolder(view); } } @Override public void onBindViewHolder(DateViewHolder holder, int position) { LabelerDate labelerDate = dateDataList.get(position); if (getItemViewType(position) == VIEW_TYPE_ITEM) { if(labelerDate.dateType.equals(BirthDayActivity.DateType.C31)) holder.tvDate.setText(String.valueOf(labelerDate.valueDate)); holder.tvDate.setVisibility(View.VISIBLE); holder.imgSmall.setVisibility(View.VISIBLE); if (position == selectedItem) { holder.tvDate.setTextColor(Color.parseColor("#094673")); holder.tvDate.setTextSize(35); holder.imgSmall.setBackgroundResource(R.color.textviewbold); } else { holder.tvDate.setTextColor(Color.GRAY); holder.tvDate.setTextSize(35); holder.imgSmall.setBackgroundResource(R.color.gray); } } } public void setSelecteditem(int selecteditem) { this.selectedItem = selecteditem; notifyDataSetChanged(); } @Override public int getItemCount() { return dateDataList.size(); } @Override public int getItemViewType(int position) { LabelerDate labelerDate = dateDataList.get(position); if (labelerDate.dateType.equals(BirthDayActivity.DateType.NONE)) { return VIEW_TYPE_PADDING; } return VIEW_TYPE_ITEM; } public class DateViewHolder extends RecyclerView.ViewHolder { public TextView tvDate; public ImageView imgSmall; public DateViewHolder(View itemView) { super(itemView); tvDate = (TextView) itemView.findViewById(R.id.tvNumberDate); imgSmall = (ImageView) itemView.findViewById(R.id.small_marked_dob); } }}

Este es el alogrithm más importante:

public void getRecyclerviewDate() { recyclerViewDate = (RecyclerView) findViewById(R.id.recyclerViewDay); ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver(); vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this); finalWidthDate = recyclerViewDate.getMeasuredWidth(); itemWidthDate = getResources().getDimension(R.dimen.item_dob_width); paddingDate = (finalWidthDate - itemWidthDate) / 2; firstItemWidthDate = paddingDate ; allPixelsDate = 0; final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext()); dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerViewDate.setLayoutManager(dateLayoutManager); recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); synchronized (this) { if(newState == RecyclerView.SCROLL_STATE_IDLE){ calculatePositionAndScrollDate(recyclerView); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); allPixelsDate += dx; } }); if (labelerDates == null) labelerDates = new ArrayList<>(); labelerDates.addAll(genLabelerDate(currentMonth, currentYear)); dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate); recyclerViewDate.setAdapter(dateAdapter); return true; } }); } /* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/ private void calculatePositionAndScrollDate(RecyclerView recyclerView) { int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate); if (expectedPositionDate == -1) { expectedPositionDate = 0; } else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) { expectedPositionDate--; } scrollListToPositionDate(recyclerView, expectedPositionDate); } /* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/ private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) { float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate; float missingPxDate = targetScrollPosDate - allPixelsDate; if (missingPxDate != 0) { recyclerView.smoothScrollBy((int) missingPxDate, 0); } } private void setDateValue() { int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate); setColorDate = expectedPositionDateColor + 1; //set color here dateAdapter.setSelecteditem(setColorDate); } @Override protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); allPixelsDate = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE); allPixelsDateChanged = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED); } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putFloat(BUNDLE_LIST_PIXELS_DATE, allPixelsDate); outState.putFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED, allPixelsDateChanged); }

Y este es mi resultado:

Mira este link video, esta es mi demo de la aplicación


No creo que haya un enfoque directo para lograr esto. Puedes intentar esto -

listView.setOnScrollListener (oyente);

@Anular

public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) { int firstVisibleRow = listView.getFirstVisiblePosition(); int lastVisibleRow = listView.getLastVisiblePosition(); int count - ((int*)count/2; int middle = listview.getChildAt(count - ((int)count/2)); } }

También puede consultar esta respuesta https://.com/a/13134100/1320616 https://.com/a/19205940/1320616



Si alguien está buscando una implementación más genérica, aquí está mi código basado en las respuestas de este hilo:

Agregue CenterLinearSnapHelper

public class CenterLinearSnapHelper extends LinearSnapHelper { //Constants public static final String TAG = CenterLinearSnapHelper.class.getSimpleName(); //Attributes private Context context; private float itemWidth; private OnPaddingComputationListener listener; //Constructors /** * A linear snap helper which helps centering the items in a recyclerview. * * @param itemWidth The (fixed) width of a child view in pixels. */ public CenterLinearSnapHelper(float itemWidth) { this.itemWidth = itemWidth; } public void attachToRecyclerView(@Nullable RecyclerView recyclerView, @NonNull OnPaddingComputationListener listener) throws IllegalStateException { this.listener = listener; //Calculates the padding for the first and end item calculatePadding(recyclerView); //Create a LinearSnapHelper and attach the recyclerView to it. attachToRecyclerView(recyclerView); } public float getItemWidth() { return itemWidth; } private void calculatePadding(RecyclerView recyclerView) { if (recyclerView == null) return; ViewTreeObserver observer = recyclerView.getViewTreeObserver(); observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { recyclerView.getViewTreeObserver().removeOnPreDrawListener(this); int finalWidth = recyclerView.getMeasuredWidth(); float padding = (finalWidth - itemWidth) / 2; listener.onPadding(padding, finalWidth); return true; } }); } public interface OnPaddingComputationListener { void onPadding(float padding, int finalWidth); } }

En su Actividad / Fragmento donde crea su RecyclerView:

float itemWidth = getResources().getDimension(R.dimen.favorite_room_width); CenterLinearSnapHelper snapHelper = new CenterLinearSnapHelper(itemWidth); snapHelper.attachToRecyclerView(binding.listFavorites, (padding, finalWidth) -> { //Set the adapter roomAdapter = new RoomAdapter(requireContext(), rooms); roomAdapter.addPaddingItems((int) padding); roomAdapter.setOnToggleClickListener(FavoritesFragment.this); binding.listFavorites.setAdapter(roomAdapter); });

En tu adaptador:

public void addPaddingItems(int padding) { if (padding < 0) throw new IllegalStateException("Padding cannot be smaller than 0"); this.padding = padding; //Add 2 new items as the first and last //NOTE: If you update your existing dataset (e.g add new items), you should redo the calculation! rooms.add(0, new Room("First")); rooms.add(rooms.size(), new Room("Last")); } @Override public int getItemViewType(int position) { if (padding >= 0 && (position == 0 || position == rooms.size() - 1)) { return VIEW_TYPE_PADDING; } return VIEW_TYPE_ITEM; } @NonNull @Override public RoomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { ViewFavoriteRoomBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_favorite_room, parent, false); if (viewType == VIEW_TYPE_PADDING) { RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) binding.getRoot().getLayoutParams(); layoutParams.width = padding; binding.getRoot().setLayoutParams(layoutParams); } RoomViewHolder viewHolder = new RoomViewHolder(context, binding, onToggleClickListener); viewHolder.getRecyclerView().setRecycledViewPool(viewPool); return viewHolder; }



USO DE SNAPHELPER: UNA SOLUCIÓN SUAVE

Aquí hay otra solución que usa SnapHelper . A partir de la respuesta de @TranHieu aquí:

https://.com/a/34647005/3944251

y comprimido por @ sector11 aquí:

https://.com/a/38411582/3944251

Escribí el siguiente código, que también se basa en las dos respuestas anteriores, pero es más simple y ofrece una solución más fluida usando SnapHelper presentado en la biblioteca de soporte android 24.2.0 .

Aquí tienes la clase MainActivity . El resto es lo mismo con la respuesta de @ sector11.

import android.graphics.Color; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearSnapHelper; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.TextView; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); public float firstItemWidthDate; public float itemWidthDate; public int allPixelsDate; public int finalWidthDate; private DateAdapter dateAdapter; private ArrayList<LabelerDate> labelerDates; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); labelerDates = new ArrayList<>(); getRecyclerviewDate(); } public void getRecyclerviewDate() { final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date); recyclerViewDate.postDelayed(new Runnable() { @Override public void run() { //recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1); setDateValue(); } }, 300); ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver(); vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this); finalWidthDate = recyclerViewDate.getMeasuredWidth(); itemWidthDate = getResources().getDimension(R.dimen.item_db_width); firstItemWidthDate = (finalWidthDate - itemWidthDate) / 2; allPixelsDate = 0; final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext()); dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerViewDate.setLayoutManager(dateLayoutManager); /* Create a LinearSnapHelper and attach the recyclerView to it. */ final LinearSnapHelper snapHelper = new LinearSnapHelper(); snapHelper.attachToRecyclerView(recyclerViewDate); recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { allPixelsDate += dx; recyclerView.post(new Runnable() { public void run() { setDateValue(); } }); } }); genLabelerDate(); dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate); recyclerViewDate.setAdapter(dateAdapter); dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1); return true; } }); } private void genLabelerDate() { for (int i = 0; i < 32; i++) { LabelerDate labelerDate = new LabelerDate(); labelerDate.setNumber(Integer.toString(i)); labelerDates.add(labelerDate); if (i == 0 || i == 31) { labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING); } else { labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM); } } } // private void setDateValue() { int expectedPositionDateColor = Math.round(allPixelsDate / itemWidthDate); int setColorDate = expectedPositionDateColor + 1; // set color here dateAdapter.setSelecteditem(setColorDate); } public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> { private ArrayList<LabelerDate> dateDataList; private static final int VIEW_TYPE_PADDING = 1; private static final int VIEW_TYPE_ITEM = 2; private int paddingWidthDate = 0; private int selectedItem = -1; public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) { this.dateDataList = dateData; this.paddingWidthDate = paddingWidthDate; } @Override public DateAdapter.DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); if (viewType == VIEW_TYPE_PADDING) { RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams(); layoutParams.width = paddingWidthDate; view.setLayoutParams(layoutParams); } return new DateViewHolder(view); } @Override public void onBindViewHolder(DateAdapter.DateViewHolder holder, int position) { LabelerDate labelerDate = dateDataList.get(position); if (getItemViewType(position) == VIEW_TYPE_ITEM) { holder.tvDate.setText(labelerDate.getNumber()); holder.tvDate.setVisibility(View.VISIBLE); Log.d(TAG, "default " + position + ", selected " + selectedItem); if (position == selectedItem) { Log.d(TAG, "center" + position); holder.tvDate.setTextColor(Color.parseColor("#76FF03")); holder.tvDate.setTextSize(35); } else { holder.tvDate.setTextColor(Color.WHITE); holder.tvDate.setTextSize(18); } } else { holder.tvDate.setVisibility(View.INVISIBLE); } } public void setSelecteditem(int selecteditem) { this.selectedItem = selecteditem; notifyDataSetChanged(); } @Override public int getItemCount() { return dateDataList.size(); } @Override public int getItemViewType(int position) { LabelerDate labelerDate = dateDataList.get(position); if (labelerDate.getType() == VIEW_TYPE_PADDING) { return VIEW_TYPE_PADDING; } else { return VIEW_TYPE_ITEM; } } public class DateViewHolder extends RecyclerView.ViewHolder { public TextView tvDate; public DateViewHolder(View itemView) { super(itemView); tvDate = (TextView) itemView.findViewById(R.id.txt_date); } } } private class LabelerDate { private int type; private String number; public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public int getType() { return type; } public void setType(int type) { this.type = type; } } }