Подтвердить что ты не робот

Изменение ViewPager для включения бесконечной прокрутки страницы

Джон Уиллис опубликовал сообщение о том, как включить бесконечную прокрутку с его кодом. Там он сказал, что внес некоторые изменения в класс ViewPager в библиотеку поддержки Android. Какие изменения были внесены и как можно "перекомпилировать" библиотеку с изменением ViewPager?

4b9b3361

Ответ 1

Спасибо за ваш ответ Шериф.

Я решил это немного по-другому.

Я изменил код класса ViewPager библиотеки поддержки Android. Метод setCurrentItem(int)

изменяет страницу с помощью анимации. Этот метод вызывает внутренний метод, для которого требуется индекс и флаг, обеспечивающий плавную прокрутку. Этот флаг boolean smoothScroll. Расширение этого метода вторым параметром boolean smoothScroll разрешило его для меня. Вызов этого метода setCurrentItem(int index, boolean smoothScroll) позволил мне прокручивать его бесконечно.

Вот полный пример:

Обратите внимание, что отображается только центральная страница. Кроме того, я хранил страницы отдельно, позволяя мне справляться с ними с большей легкостью.

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

Однако, без кода Джона Уиллиса я бы не решил его сам.

EDIT: здесь blogpost об этом:

Ответ 2

Я решил эту проблему очень просто, используя небольшой взлом в адаптере. Вот мой код:

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

И затем, в ViewPager, мы устанавливаем текущую страницу посередине:

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

Ответ 3

Бесконечный просмотр пейджера путем переопределения 4 адаптивных методов в существующем классе адаптеров

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

Ответ 4

Все, что вам нужно сделать, это посмотреть на здесь

Вы увидите, что в строке 295 страница всегда установлена ​​в 1, так что она прокручивается и что количество страниц равно 3 в getCount().

Это две основные вещи, которые вам нужно изменить, остальное - ваша логика, и вы можете обращаться с ними по-другому.

Просто создайте персональный счетчик, который подсчитывает реальную страницу, на которой вы находитесь, потому что позиция больше не будет использоваться после постоянной установки текущей страницы в 1 на строке 295.

p.s. этот код не мой, на него ссылался вопрос, связанный в вашем вопросе

Ответ 5

На самом деле, я смотрел на различные способы сделать эту "бесконечную" разбивку на страницы, и хотя человеческое понятие времени состоит в том, что оно бесконечно (хотя у нас есть понятие начала и конца времени), компьютеры имеют дело с дискретностью. Существует минимальное и максимальное время (которое можно отрегулировать с течением времени, помните, основываясь на страхе Y2K?).

В любом случае, точка этого обсуждения состоит в том, что он/должен быть достаточным для поддержки относительно бесконечного диапазона дат через фактически конечный диапазон дат. Отличным примером этого является реализация платформы Android CalendarView и WeeksAdapter внутри нее. Минимальная дата по умолчанию - 1900, а максимальная дата по умолчанию - 2100, это должно охватывать 99% использования календаря любого человека в радиусе 10 лет вокруг сегодня.

То, что они делают в своей реализации (ориентировано на недели), вычисляет количество недель между минимальной и максимальной датой. Это становится числом страниц в пейджере. Помните, что пейджер не должен поддерживать все эти страницы одновременно (setOffscreenPageLimit(int)), он просто должен иметь возможность создавать страницу на основе номера страницы (или индекса/позиции). В этом случае индекс - это количество недель, прошедших с минимальной даты. При таком подходе вам просто нужно сохранить минимальную дату и количество страниц (расстояние до максимальной даты), а затем для любой страницы вы можете легко вычислить неделю, связанную с этой страницей. Не танцует вокруг того, что ViewPager не поддерживает цикл (a.k.a бесконечная разбивка на страницы) и пытается заставить его вести себя так, как будто он может бесконечно прокручиваться.

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

Тогда WeekDaysFragment может легко отображать неделю, начиная с даты, переданной в ее аргументах.

В качестве альтернативы, похоже, что в какой-либо версии приложения Calendar на Android используется ViewSwitcher (что означает только 2 страницы, тот, который вы видите, и скрытую страницу). Затем он изменяет анимацию перехода, основываясь на том, каким образом пользователь вычистил и соответственно отобразил следующую/предыдущую страницу. Таким образом, вы получаете бесконечную разбивку на страницы, потому что она просто переключается между двумя страницами бесконечно. Для этого требуется использовать View для страницы, поэтому я пошел с первым подходом.

В общем, если вы хотите "бесконечную разбивку на страницы", это, вероятно, потому, что ваши страницы основаны на датах или временах как-то. Если это так, рассмотрим использование конечного подмножества времени, которое относительно бесконечно. Например, CalendarView реализуется. Или вы можете использовать подход ViewSwitcher. Преимущество этих двух подходов состоит в том, что ничто не особенно необычно для ViewSwitcher или ViewPager, и не требует никаких трюков или повторной реализации, чтобы заставить их вести себя бесконечно (ViewSwitcher уже предназначен для бесконечного переключения между представлениями, но ViewPager предназначен для работы с конечным, но не обязательно постоянным набором страниц).

Ответ 6

бесконечный скелет адаптера слайдера на основе предыдущих образцов

некоторые критические проблемы:

  • запомнить оригинальную (относительную) позицию в просмотре страницы (тег, используемый в образце), поэтому мы будем рассматривать эту позицию для определения относительного положения вида. иначе дочерний порядок в пейджере будет смешанным.
  • необходимо заполнить первый абсолютный вид внутри адаптера. (в остальное время это заполнение будет недействительным) не нашел способа заставить его заполнить из обработчика пейджера. время останова абсолютное представление будет переопределено из обработчика пейджера с правильными значениями.
  • Когда страницы быстро перемещаются, боковая страница (фактически слева) не заполняется из обработчика пейджера. нет обходного пути на данный момент, просто используйте пустой вид, он будет заполнен фактическими значениями, когда перетаскивание остановлено. upd: быстрое обходное решение: отключить адаптер destroyItem.

вы можете посмотреть лог-код, чтобы понять, что происходит в этом примере

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

И затем:

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

Ответ 7

взломан 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 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>

Готово

Ответ 8

Для бесконечной прокрутки с днями важно, чтобы у вас был хороший фрагмент в пейджере, поэтому я написал свой ответ на этой странице (Viewpager в Android, чтобы переключаться между днями бесконечно)

Он работает очень хорошо! Выше ответы не работали для меня, поскольку я хотел, чтобы это работало.

Ответ 9

Я построил библиотеку, которая может создавать любые ViewPager, pagerAdapter (или FragmentStatePagerAdapter) и необязательную TabLayout бесконечно прокручивать.

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