Changer ViewPager pour activer le défilement infini de la page

Jon Willis a posté sur comment activer un scrolling infini avec son code. Là, il a dit qu'il a fait quelques changements dans la classe ViewPager dans la bibliothèque de soutien android. Quelles modifications ont été apportées et comment est-il possible de "recompiler" la bibliothèque avec le changement de ViewPager?

39
demandé sur Suragch 2011-10-14 14:56:52

9 réponses

j'ai résolu ce problème très simplement en utilisant un petit hack dans l'adaptateur. Voici mon code:

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

et puis, dans le ViewPager, nous mettons la page actuelle au milieu:

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
35
répondu petrnohejl 2012-03-04 14:59:47

Merci pour votre réponse Shereef.

j'ai résolu un peu différemment.

j'ai changé le code de la classe ViewPager de la bibliothèque de soutien android. La méthode setCurrentItem(int)

modifie la page avec l'animation. Cette méthode appelle une méthode interne qui nécessite l'index et d'un indicateur permettant de défilement lisse. Ce drapeau est boolean smoothScroll . L'extension de cette méthode avec un deuxième paramètre boolean smoothScroll résolu m'. Appeler cette méthode setCurrentItem(int index, boolean smoothScroll) m'a permis de la faire défiler indéfiniment.

Voici un exemple complet:

veuillez considérer que seule la page centrale est affichée. De plus, est-ce que j'ai stocké les pages séparément, ce qui m'a permis de les traiter avec plus de facilité.

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

cependant, sans le Code de Jon Willis, Je ne l'aurais pas résolu moi-même.

EDIT: voici un blogpost about ceci:

31
répondu thehayro 2016-08-17 12:43:05

pageur de vue infinie en surpassant 4 méthodes d'adaptateur dans votre classe d'adaptateur existante

    @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);
    }
27
répondu Pawan Maheshwari 2013-07-02 11:34:52

Tout ce que vous devez faire est de regarder l'exemple ici

vous constaterez qu'à la ligne 295 la page est toujours positionnée à 1 de sorte qu'elle soit scrollable et que le nombre de pages est de 3 dans la méthode getCount() .

ce sont les 2 choses principales que vous devez changer, le reste est votre logique et vous pouvez les gérer différemment.

il suffit de faire un compteur personnel qui compte la page réelle que vous êtes sur parce que la position ne sera plus utilisable après avoir toujours réglé la page courante à 1 sur la ligne 295.

p. S. ce code n'est pas le mien il a été référencé dans la question que vous avez liée dans votre question

10
répondu Shereef Marzouk 2016-08-17 12:09:12

en fait, j'ai regardé les différentes façons de faire cette pagination "infinie", et même si la notion humaine du temps est qu'il est infini (même si nous avons une notion du début et de la fin du temps), les ordinateurs traitent dans le discret. Il y a un temps minimum et un temps maximum (qui peuvent être ajustés au fil du temps, vous vous souvenez de la base de la peur de L'an 2000?).

de toute façon, le point de cette discussion est qu'il est / devrait être suffisant pour soutenir un plage de dates infinies à travers une plage de dates réellement finie. Un bon exemple de cela est la mise en œuvre CalendarView D'Android framework, et le WeeksAdapter à l'intérieur de celui-ci. La date minimale par défaut est en 1900 et la date maximale par défaut est en 2100, cela devrait couvrir 99% de l'utilisation du calendrier de n'importe qui dans un rayon de 10 ans autour d'aujourd'hui facilement.

ce qu'ils font dans leur mise en œuvre (axée sur les semaines) est de calculer le nombre de semaines entre le minimum et le maximum date. Cela devient le nombre de pages du pager. Rappelez-vous que le pager n'a pas besoin de maintenir toutes ces pages simultanément ( setOffscreenPageLimit(int) ), il a juste besoin d'être en mesure de créer la page basée sur le numéro de page (ou index/position). Dans ce cas, l'indice est le nombre de semaines que la semaine est à partir de la date minimale. Avec cette approche, vous avez juste à maintenir la date minimale et le nombre de pages (distance à la date maximale), puis pour toute page, vous pouvez facilement calculer la semaine associé à cette page. Pas de danse autour du fait que ViewPager ne supporte pas la boucle (A. K. une pagination infinie), et essayer de la forcer à se comporter comme elle peut défiler à l'infini.

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

puis WeekDaysFragment peut facilement afficher la semaine à partir de la date passée dans ses arguments.

alternativement, il semble qu'une version de L'application Calendrier sur Android utilise un ViewSwitcher (ce qui signifie qu'il n'y a que 2 pages, celle que vous voir et la page cachée). Il modifie ensuite l'animation de transition en fonction de la façon dont l'Utilisateur a glissé et rend la page suivante/précédente en conséquence. De cette façon, vous obtenez pagination infinie parce qu'il suffit de passer entre deux pages infiniment. Cela nécessite d'utiliser un View pour la page cependant, qui est la façon dont je suis allé avec la première approche.

en général, si vous voulez une" pagination infinie", c'est probablement parce que vos pages sont basées sur des dates ou des heures. Si c'est le cas, envisagez d'utiliser un sous-ensemble fini de temps qui est relativement infini à la place. C'est ainsi que CalendarView est mis en œuvre par exemple. Ou vous pouvez utiliser l'approche ViewSwitcher . L'avantage de ces deux approches est que ni l'un ni l'autre ne fait quelque chose de particulièrement inhabituel avec le ViewSwitcher ou ViewPager , et ne nécessite pas de trucs ou de réimpression pour les forcer à se comporter à l'infini ( ViewSwitcher est déjà conçu pour basculer entre les vues à l'infini, mais ViewPager est conçu pour travailler sur un fini, mais pas nécessairement constante, ensemble de pages).

5
répondu Dandre Allison 2012-12-26 22:42:00

squelette d'adaptateur de curseur infini basé sur des échantillons précédents

quelques questions essentielles:

  • se souvenir de la position initiale (relative) dans la vue page (étiquette utilisée dans l'échantillon), nous allons donc regarder cette position pour définir la position relative de la vue. sinon l'ordre de l'enfant dans le pager est mixte
  • doit remplir pour la première fois la vue absolue à l'intérieur de l'adaptateur. (le reste du temps ce remplissage ne sera pas valide) trouvé aucun moyen de le forcer remplir de pager gestionnaire. les temps de repos de la vue absolue seront annulés du gestionnaire de pager avec les valeurs correctes.
  • lorsque les pages sont glissées rapidement, la page latérale (en fait à gauche) n'est pas remplie à partir du pager handler. pas de solution de contournement pour le moment, il suffit d'utiliser la vue vide, elle sera remplie de valeurs réelles Lorsque la traînée est arrêtée. upd: solution rapide: désactiver le destroyItem de l'adaptateur.

vous pouvez regarder le logcat pour comprendre ce qui se passe dans ce échantillon

<?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>

et ensuite:

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);
    }
}
2
répondu Dmitry Bryliuk 2015-11-03 09:11:52

Son piraté par CustomPagerAdapter :

activité principale.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>

fait

1
répondu Hiren Patel 2015-12-03 08:36:30

pour le défilement infini avec des jours il est important que vous avez le bon fragment dans le pager donc j'ai écrit ma réponse sur cette page ( Viewpager dans Android pour passer entre les jours sans fin )

Ça marche très bien! Les réponses ci-dessus n'ont pas fonctionné pour moi car je voulais qu'il fonctionne.

0
répondu Richard Lindhout 2017-05-23 12:26:31

j'ai construit une bibliothèque qui peut faire N'importe quel ViewPager, pagerAdapter (ou FragmentStatePagerAdapter), et TabLayout en option le défilement infini.

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

0
répondu Brad 2017-06-15 19:38:19