Джон Уиллис опубликовал сообщение о том, как включить бесконечную прокрутку с его кодом. Там он сказал, что внес некоторые изменения в класс ViewPager в библиотеку поддержки Android. Какие изменения были внесены и как можно "перекомпилировать" библиотеку с изменением ViewPager?
Изменение ViewPager для включения бесконечной прокрутки страницы
Ответ 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