Cambiar ViewPager para habilitar el desplazamiento de páginas infinito

Jon Willis ha publicado sobre cómo habilitar un desplazamiento infinito con su código. Allí dijo que hizo algunos cambios en la clase ViewPager en la biblioteca de soporte de android. ¿Qué cambios se han realizado y cómo es posible "recompilar" la biblioteca con el cambio de ViewPager?

Gracias

He resuelto este problema muy simplemente con un pequeño hack en el adaptador. Aquí está mi código:

public class MyPagerAdapter extends FragmentStatePagerAdapter { public static int LOOPS_COUNT = 1000; private ArrayList<Product> mProducts; public MyPagerAdapter(FragmentManager manager, ArrayList<Product> products) { super(manager); mProducts = products; } @Override public Fragment getItem(int position) { if (mProducts != null && mProducts.size() > 0) { position = position % mProducts.size(); // use modulo for infinite cycling return MyFragment.newInstance(mProducts.get(position)); } else { return MyFragment.newInstance(null); } } @Override public int getCount() { if (mProducts != null && mProducts.size() > 0) { return mProducts.size()*LOOPS_COUNT; // simulate infinite by big number of products } else { return 1; } } } 

Y luego, en ViewPager, ponemos la página actual en el centro:

 mAdapter = new MyPagerAdapter(getSupportFragmentManager(), mProducts); mViewPager.setAdapter(mAdapter); mViewPager.setCurrentItem(mViewPager.getChildCount() * MyPagerAdapter.LOOPS_COUNT / 2, false); // set current item in the adapter to middle 

Gracias por su respuesta Shereef.

Lo solucioné un poco diferente.

Cambié el código de la clase ViewPager de la biblioteca de soporte de android. El método setCurrentItem(int)

Cambia la página con animación. Este método llama a un método interno que requiere el índice y un indicador que permite desplazamiento suave. Este indicador es boolean smoothScroll . Extender este método con un segundo parámetro boolean smoothScroll resolvió para mí. Llamar a este método setCurrentItem(int index, boolean smoothScroll) me permitió hacer que se desplazara indefinidamente.

Aquí hay un ejemplo completo:

Tenga en cuenta que sólo se muestra la página central. Además he almacenado las páginas por separado, lo que me permite manejar con más facilidad.

 private class Page { View page; List<..> data; } // page for predecessor, current, and successor Page[] pages = new Page[3]; mDayPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_IDLE) { if (mFocusedPage == 0) { // move some stuff from the // center to the right here moveStuff(pages[1], pages[2]); // move stuff from the left to the center moveStuff(pages[0], pages[1]); // retrieve new stuff and insert it to the left page insertStuff(pages[0]); } else if (mFocusedPage == 2) { // move stuff from the center to the left page moveStuff(pages[1], pages[0]); // move stuff from the right to the center page moveStuff(pages[2], pages[1]); // retrieve stuff and insert it to the right page insertStuff(pages[2]); } // go back to the center allowing to scroll indefinitely mDayPager.setCurrentItem(1, false); } } }); 

Sin embargo, sin el código de Jon Willis no lo habría resuelto yo mismo.

EDIT: aquí hay un blogpost sobre esto:

Pager de vista infinita reemplazando 4 métodos de adaptador en la clase de adaptador existente

  @Override public int getCount() { return Integer.MAX_VALUE; } @Override public CharSequence getPageTitle(int position) { String title = mTitleList.get(position % mActualTitleListSize); return title; } @Override public Object instantiateItem(ViewGroup container, int position) { int virtualPosition = position % mActualTitleListSize; return super.instantiateItem(container, virtualPosition); } @Override public void destroyItem(ViewGroup container, int position, Object object) { int virtualPosition = position % mActualTitleListSize; super.destroyItem(container, virtualPosition, object); } 

Todo lo que necesitas hacer es mirar el ejemplo aquí

Encontrará que en la línea 295 la página siempre se establece en 1 para que sea desplazable y que la cuenta de páginas sea 3 en el método getCount() .

Esas son las 2 cosas principales que usted necesita cambiar, el resto es su lógica y usted puede manejarlas diferentemente.

Basta con hacer un contador personal que cuenta la página real que está en la posición porque ya no será utilizable después de siempre la página actual a 1 en la línea 295.

Ps este código no es el mío que se hizo referencia en la pregunta que enlazó en su pregunta

En realidad, he estado observando las diversas maneras de hacer esta paginación "infinita", y aunque la noción humana de tiempo es que es infinita (aunque tengamos una noción de principio y fin de tiempo), las computadoras tratan En el discreto. Hay un tiempo mínimo y máximo (que se puede ajustar a medida que pasa el tiempo, recuerda la base del susto Y2K?).

De todos modos, el punto de esta discusión es que es / debe ser suficiente para soportar un intervalo de fecha relativamente infinito a través de un rango de fechas realmente finito. Un gran ejemplo de esto es la implementación de CalendarView Android framework y el WeeksAdapter dentro de ella. La fecha mínima predeterminada es en 1900 y la fecha máxima predeterminada es en 2100, esto debería cubrir el 99% del uso del calendario de cualquier persona dentro de un radio de 10 años alrededor de hoy fácilmente.

Lo que hacen en su implementación (centrada en semanas) es computar el número de semanas entre la fecha mínima y la fecha máxima. Esto se convierte en el número de páginas en el buscapersonas. Recuerde que el buscapersonas no necesita mantener todas estas páginas simultáneamente ( setOffscreenPageLimit(int) ), sólo tiene que ser capaz de crear la página basada en el número de página (o índice / posición). En este caso el índice es el número de semanas que la semana es desde la fecha mínima. Con este enfoque sólo tiene que mantener la fecha mínima y el número de páginas (distancia a la fecha máxima), luego para cualquier página que pueda calcular fácilmente la semana asociada con esa página. No bailar en torno al hecho de que ViewPager no soporta bucle (aka paginación infinita), y tratando de forzar a comportarse como puede desplazarse infinitamente.

 new FragmentStatePagerAdapter(getFragmentManager()) { @Override public Fragment getItem(int index) { final Bundle arguments = new Bundle(getArguments()); final Calendar temp_calendar = Calendar.getInstance(); temp_calendar.setTimeInMillis(_minimum_date.getTimeInMillis()); temp_calendar.setFirstDayOfWeek(_calendar.getStartOfWeek()); temp_calendar.add(Calendar.WEEK_OF_YEAR, index); // Moves to the first day of this week temp_calendar.add(Calendar.DAY_OF_YEAR, -UiUtils.modulus(temp_calendar.get(Calendar.DAY_OF_WEEK) - temp_calendar.getFirstDayOfWeek(), 7)); arguments.putLong(KEY_DATE, temp_calendar.getTimeInMillis()); return Fragment.instantiate(getActivity(), WeekDaysFragment.class.getName(), arguments); } @Override public int getCount() { return _total_number_of_weeks; } }; 

Entonces WeekDaysFragment puede mostrar fácilmente la semana comenzando en la fecha pasada en sus argumentos.

Como alternativa, parece que alguna versión de la aplicación Calendario en Android utiliza ViewSwitcher (lo que significa que sólo hay 2 páginas, la que ve y la página oculta). A continuación, cambia la animación de transición en función de la forma en que el usuario pasó y procesa la página siguiente / anterior en consecuencia. De esta manera se obtiene paginación infinita porque simplemente cambia entre dos páginas infinitamente. Esto requiere usar una View para la página sin embargo, que es la manera que fui con el primer acercamiento.

En general, si desea "paginación infinita", es probablemente porque sus páginas se basan en fechas o tiempos de alguna manera. Si este es el caso considere el uso de un subconjunto finito de tiempo que es relativamente infinito en su lugar. Así es como CalendarView se implementa por ejemplo. O puede utilizar el enfoque ViewSwitcher . La ventaja de estos dos enfoques es que tampoco hace nada particularmente inusual con ViewSwitcher o ViewPager , y no requiere ningún truco o reimplementación para obligarlos a comportarse infinitamente ( ViewSwitcher ya está diseñado para cambiar entre las vistas infinitamente, pero ViewPager está diseñado para Trabajo en un finito, pero no necesariamente constante, conjunto de páginas).

Esqueleto adaptador infinito adaptador basado en muestras anteriores

Algunas cuestiones críticas:

  • Recuerde la posición original (relativa) en la vista de página (etiqueta usada en la muestra), así que buscaremos esta posición para definir la posición relativa de la vista. De lo contrario, el orden de los niños en el buscapersonas está mezclado
  • Tiene que llenar la primera vista absoluta vista dentro del adaptador. (El resto de veces este relleno será inválido) no encontró manera de forzar el relleno desde el manejador de paginación. La vista absoluta de tiempos de descanso se anulará del manejador de paginador con valores correctos.
  • Cuando las páginas se deslizan rápidamente, la página lateral (en realidad la izquierda) no se rellena desde el controlador de paginación. No hay ninguna solución por el momento, solo usa la vista vacía, se llenará con valores reales cuando se detenga el arrastre. Upd: solución rápida: deshabilita el destroyItem del adaptador.

Usted puede mirar el logcat para entender lo que está sucediendo en esta muestra

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/calendar_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" android:padding="5dp" android:layout_gravity="center_horizontal" android:text="Text Text Text" /> </RelativeLayout> 

Y entonces:

 public class ActivityCalendar extends Activity { public class CalendarAdapter extends PagerAdapter { @Override public int getCount() { return 3; } @Override public boolean isViewFromObject(View view, Object object) { return view == ((RelativeLayout) object); } @Override public Object instantiateItem(ViewGroup container, int position) { LayoutInflater inflater = (LayoutInflater)ActivityCalendar.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View viewLayout = inflater.inflate(R.layout.layout_calendar, container, false); viewLayout.setTag(new Integer(position)); //TextView tv = (TextView) viewLayout.findViewById(R.id.calendar_text); //tv.setText(String.format("Text Text Text relative: %d", position)); if (!ActivityCalendar.this.scrolledOnce) { // fill here only first time, the rest will be overriden in pager scroll handler switch (position) { case 0: ActivityCalendar.this.setPageContent(viewLayout, globalPosition - 1); break; case 1: ActivityCalendar.this.setPageContent(viewLayout, globalPosition); break; case 2: ActivityCalendar.this.setPageContent(viewLayout, globalPosition + 1); break; } } ((ViewPager) container).addView(viewLayout); //Log.i("instantiateItem", String.format("position = %d", position)); return viewLayout; } @Override public void destroyItem(ViewGroup container, int position, Object object) { ((ViewPager) container).removeView((RelativeLayout) object); //Log.i("destroyItem", String.format("position = %d", position)); } } public void setPageContent(View viewLayout, int globalPosition) { if (viewLayout == null) return; TextView tv = (TextView) viewLayout.findViewById(R.id.calendar_text); tv.setText(String.format("Text Text Text global %d", globalPosition)); } private boolean scrolledOnce = false; private int focusedPage = 0; private int globalPosition = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_calendar); final ViewPager viewPager = (ViewPager) findViewById(R.id.pager); viewPager.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int position) { focusedPage = position; // actual page change only when position == 1 if (position == 1) setTitle(String.format("relative: %d, global: %d", position, globalPosition)); Log.i("onPageSelected", String.format("focusedPage/position = %d, globalPosition = %d", position, globalPosition)); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { //Log.i("onPageScrolled", String.format("position = %d, positionOffset = %f", position, positionOffset)); } @Override public void onPageScrollStateChanged(int state) { Log.i("onPageScrollStateChanged", String.format("state = %d, focusedPage = %d", state, focusedPage)); if (state == ViewPager.SCROLL_STATE_IDLE) { if (focusedPage == 0) globalPosition--; else if (focusedPage == 2) globalPosition++; scrolledOnce = true; for (int i = 0; i < viewPager.getChildCount(); i++) { final View v = viewPager.getChildAt(i); if (v == null) continue; // reveal correct child position Integer tag = (Integer)v.getTag(); if (tag == null) continue; switch (tag.intValue()) { case 0: setPageContent(v, globalPosition - 1); break; case 1: setPageContent(v, globalPosition); break; case 2: setPageContent(v, globalPosition + 1); break; } } Log.i("onPageScrollStateChanged", String.format("globalPosition = %d", globalPosition)); viewPager.setCurrentItem(1, false); } } }); CalendarAdapter calendarAdapter = this.new CalendarAdapter(); viewPager.setAdapter(calendarAdapter); // center item viewPager.setCurrentItem(1, false); } } 

Su hackeado por CustomPagerAdapter :

MainActivity.java :

 import android.content.Context; import android.os.Handler; import android.os.Parcelable; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private List<String> numberList = new ArrayList<String>(); private CustomPagerAdapter mCustomPagerAdapter; private ViewPager mViewPager; private Handler handler; private Runnable runnable; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); numberList.clear(); for (int i = 0; i < 10; i++) { numberList.add(""+i); } mViewPager = (ViewPager)findViewById(R.id.pager); mCustomPagerAdapter = new CustomPagerAdapter(MainActivity.this); EndlessPagerAdapter mAdapater = new EndlessPagerAdapter(mCustomPagerAdapter); mViewPager.setAdapter(mAdapater); mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { int modulo = position%numberList.size(); Log.i("Current ViewPager View's Position", ""+modulo); } @Override public void onPageScrollStateChanged(int state) { } }); handler = new Handler(); runnable = new Runnable() { @Override public void run() { mViewPager.setCurrentItem(mViewPager.getCurrentItem()+1); handler.postDelayed(runnable, 1000); } }; handler.post(runnable); } @Override protected void onDestroy() { if(handler!=null){ handler.removeCallbacks(runnable); } super.onDestroy(); } private class CustomPagerAdapter extends PagerAdapter { Context mContext; LayoutInflater mLayoutInflater; public CustomPagerAdapter(Context context) { mContext = context; mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public int getCount() { return numberList.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == ((LinearLayout) object); } @Override public Object instantiateItem(ViewGroup container, int position) { View itemView = mLayoutInflater.inflate(R.layout.row_item_viewpager, container, false); TextView textView = (TextView) itemView.findViewById(R.id.txtItem); textView.setText(numberList.get(position)); container.addView(itemView); return itemView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((LinearLayout) object); } } private class EndlessPagerAdapter extends PagerAdapter { private static final String TAG = "EndlessPagerAdapter"; private static final boolean DEBUG = false; private final PagerAdapter mPagerAdapter; EndlessPagerAdapter(PagerAdapter pagerAdapter) { if (pagerAdapter == null) { throw new IllegalArgumentException("Did you forget initialize PagerAdapter?"); } if ((pagerAdapter instanceof FragmentPagerAdapter || pagerAdapter instanceof FragmentStatePagerAdapter) && pagerAdapter.getCount() < 3) { throw new IllegalArgumentException("When you use FragmentPagerAdapter or FragmentStatePagerAdapter, it only supports >= 3 pages."); } mPagerAdapter = pagerAdapter; } @Override public void destroyItem(ViewGroup container, int position, Object object) { if (DEBUG) Log.d(TAG, "Destroy: " + getVirtualPosition(position)); mPagerAdapter.destroyItem(container, getVirtualPosition(position), object); if (mPagerAdapter.getCount() < 4) { mPagerAdapter.instantiateItem(container, getVirtualPosition(position)); } } @Override public void finishUpdate(ViewGroup container) { mPagerAdapter.finishUpdate(container); } @Override public int getCount() { return Integer.MAX_VALUE; // this is the magic that we can scroll infinitely. } @Override public CharSequence getPageTitle(int position) { return mPagerAdapter.getPageTitle(getVirtualPosition(position)); } @Override public float getPageWidth(int position) { return mPagerAdapter.getPageWidth(getVirtualPosition(position)); } @Override public boolean isViewFromObject(View view, Object o) { return mPagerAdapter.isViewFromObject(view, o); } @Override public Object instantiateItem(ViewGroup container, int position) { if (DEBUG) Log.d(TAG, "Instantiate: " + getVirtualPosition(position)); return mPagerAdapter.instantiateItem(container, getVirtualPosition(position)); } @Override public Parcelable saveState() { return mPagerAdapter.saveState(); } @Override public void restoreState(Parcelable state, ClassLoader loader) { mPagerAdapter.restoreState(state, loader); } @Override public void startUpdate(ViewGroup container) { mPagerAdapter.startUpdate(container); } int getVirtualPosition(int realPosition) { return realPosition % mPagerAdapter.getCount(); } PagerAdapter getPagerAdapter() { return mPagerAdapter; } } } 

Activity_main.xml :

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="180dp"> </android.support.v4.view.ViewPager> </RelativeLayout> 

Row_item_viewpager.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:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/txtItem" android:textAppearance="@android:style/TextAppearance.Large"/> </LinearLayout> 

Hecho

Para el desplazamiento infinito con los días es importante que tenga el buen fragmento en el buscapersonas por lo tanto escribí mi respuesta en esta página ( Viewpager en Android para cambiar entre los días sin fin )

¡Está funcionando muy bien! Las respuestas anteriores no funcionaban para mí, ya que quería que funcionara.

He construido una biblioteca que puede hacer cualquier ViewPager, pagerAdapter (o FragmentStatePagerAdapter), y TabLayout opcional infinitamente Desplazamiento.

https://github.com/memorex386/infinite-scroll-viewpager-w-tabs

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.