Android 更改ViewPager以启用无限页面滚动

Android 更改ViewPager以启用无限页面滚动,android,android-viewpager,infinite-scroll,Android,Android Viewpager,Infinite Scroll,已经发布了如何使用他的代码实现无限滚动。 在那里,他说他对android支持库中的ViewPager类做了一些更改。进行了哪些更改,以及如何使用ViewPager更改“重新编译”库?您只需查看示例即可 您会发现,在第295行中,页面始终设置为1,以便可以滚动 并且getCount()方法中的页数为3 这是你需要改变的两件主要事情,其余的是你的逻辑,你可以用不同的方式处理它们 只需制作一个个人计数器,计算您所在的真实页面,因为在第295行将当前页面设置为1后,该位置将不再可用 p、 这个代码不是我

已经发布了如何使用他的代码实现无限滚动。
在那里,他说他对android支持库中的ViewPager类做了一些更改。进行了哪些更改,以及如何使用ViewPager更改“重新编译”库?

您只需查看示例即可

您会发现,在第295行中,页面始终设置为1,以便可以滚动 并且
getCount()
方法中的页数为3

这是你需要改变的两件主要事情,其余的是你的逻辑,你可以用不同的方式处理它们

只需制作一个个人计数器,计算您所在的真实页面,因为在第295行将当前页面设置为1后,该位置将不再可用


p、 这个代码不是我的,它是在你的问题中链接的问题中引用的

谢谢你的回答谢里夫

我的解决方法有点不同

我更改了android支持库的ViewPager类的代码。方法
setCurrentItem(int)

使用动画更改页面。此方法调用一个内部方法,该方法需要索引和一个支持平滑滚动的标志。此标志为
布尔平滑滚动
。 通过使用第二个参数
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);
            }
        }
    });
私有类页面{
查看页面;
列出数据;
}
//前一页、当前页和后续页
第[]页=新的第[3]页;
mDayPager.setOnPageChangeListener(新的ViewPager.OnPageChangeListener(){
@凌驾
已选择页面上的公共无效(内部位置){
}
@凌驾
已滚动页面上的公共void(int-position、float-positionOffset、int-positionOffsetPixels){}
@凌驾
公共无效onPageScrollStateChanged(int状态){
if(state==ViewPager.SCROLL\u state\u IDLE){
如果(mFocusedPage==0){
//把一些东西搬走
//中间偏右
moveStuff(第[1]页、第[2]页);
//把东西从左边移到中间
moveStuff(第[0]页、第[1]页);
//检索新内容并将其插入左侧页面
insertStuff(第[0]页);
}
否则如果(mFocusedPage==2){
//将内容从中间移到左页
moveStuff(第[1]页、第[0]页);
//将内容从右侧移动到中间页
moveStuff(第[2]页、第[1]页);
//检索内容并将其插入到正确的页面
insertStuff(第[2]页);
}
//返回中心,允许无限滚动
mDayPager.setCurrentItem(1,false);
}
}
});
然而,如果没有乔恩·威利斯的密码,我自己也不会解决它


编辑:这里有一个关于这个的提示:

我很简单地用适配器中的一点小技巧解决了这个问题。这是我的密码:

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

事实上,我一直在研究各种各样的方法来实现“无限”分页,尽管人类对时间的概念是无限的(尽管我们有时间的开始和结束的概念),但计算机处理的是离散的。有一个最小和最大的时间(随着时间的推移可以调整,还记得Y2K恐慌的基础吗?)

无论如何,本文讨论的重点是,通过一个实际有限的日期范围来支持一个相对无限的日期范围已经足够了。一个很好的例子是Android框架的
CalendarView
实现,以及其中的
WeeksAdapter
。默认的最短日期是1900年,默认的最长日期是2100年,这应该涵盖今天周围10年半径内任何人日历使用的99%

他们在实现中所做的(重点是周数)是计算最小日期和最大日期之间的周数。这将成为寻呼机中的页数。请记住,寻呼机不需要同时维护所有这些页面(
setOffscreenPageLimit(int)
),它只需要能够基于页码(或索引/位置)创建页面。在这种情况下,索引是从最小日期算起的一周数。使用这种方法,您只需保持最小日期和页数(到最大日期的距离),然后对于任何页面,您都可以轻松计算与该页面相关联的周数。不要绕着
ViewPager
不支持循环(也称为无限分页)的事实跳舞,并试图强迫它表现得像可以无限滚动一样

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
可以轻松显示从参数中传递的日期开始的一周

或者,Android上的日历应用程序的某些版本似乎使用了一个
视图切换器
(这意味着只有两个页面,一个是你看到的,另一个是隐藏页面)。然后,它会根据用户滑动的方式更改过渡动画,并相应地呈现下一页/上一页。通过这种方式,您可以获得无限分页,因为它只是在两个页面之间无限切换。这需要对页面使用
视图
,这是我使用第一种方法的方式

一般来说,如果您想要“无限分页”,可能是因为您的页面以某种方式基于日期或时间。如果是这样的话,考虑使用有限元。
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;
    }
};
    @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);
    }
<?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);
    }
}
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;
        }

    }
}
<?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>
<?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>
class InfiniteViewPager @JvmOverloads constructor(
  context: Context,
  attrs: AttributeSet? = null
) : ViewPager(context, attrs) {
  // Allow for 100 back cycles from the beginning.
  // This should be enough to create an illusion of infinity.
  // Warning: scrolling to very high values (1,000,000+) results in strange drawing behaviour.
  private val offsetAmount get() = if (adapter?.count == 0) 0 else (adapter as InfinitePagerAdapter).realCount * 100

  override fun setAdapter(adapter: PagerAdapter?) {
    super.setAdapter(if (adapter == null) null else InfinitePagerAdapter(adapter))
    currentItem = 0
  }

  override fun setCurrentItem(item: Int) = setCurrentItem(item, false)

  override fun setCurrentItem(item: Int, smoothScroll: Boolean) {
    val adapterCount = adapter?.count

    if (adapterCount == null || adapterCount == 0) {
      super.setCurrentItem(item, smoothScroll)
    } else {
      super.setCurrentItem(offsetAmount + item % adapterCount, smoothScroll)
    }
  }

  override fun getCurrentItem(): Int {
    val adapterCount = adapter?.count

    return if (adapterCount == null || adapterCount == 0) {
      super.getCurrentItem()
    } else {
      val position = super.getCurrentItem()
      position % (adapter as InfinitePagerAdapter).realCount
    }
  }

  fun animateForward() {
    super.setCurrentItem(super.getCurrentItem() + 1, true)
  }

  fun animateBackwards() {
    super.setCurrentItem(super.getCurrentItem() - 1, true)
  }

  internal class InfinitePagerAdapter(private val adapter: PagerAdapter) : PagerAdapter() {
    internal val realCount: Int get() = adapter.count

    override fun getCount() = if (realCount == 0) 0 else Integer.MAX_VALUE

    override fun instantiateItem(container: ViewGroup, position: Int) = adapter.instantiateItem(container, position % realCount)

    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) = adapter.destroyItem(container, position % realCount, `object`)

    override fun finishUpdate(container: ViewGroup) = adapter.finishUpdate(container)

    override fun isViewFromObject(view: View, `object`: Any) = adapter.isViewFromObject(view, `object`)

    override fun restoreState(bundle: Parcelable?, classLoader: ClassLoader?) = adapter.restoreState(bundle, classLoader)

    override fun saveState(): Parcelable? = adapter.saveState()

    override fun startUpdate(container: ViewGroup) = adapter.startUpdate(container)

    override fun getPageTitle(position: Int) = adapter.getPageTitle(position % realCount)

    override fun getPageWidth(position: Int) = adapter.getPageWidth(position)

    override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) = adapter.setPrimaryItem(container, position, `object`)

    override fun unregisterDataSetObserver(observer: DataSetObserver) = adapter.unregisterDataSetObserver(observer)

    override fun registerDataSetObserver(observer: DataSetObserver) = adapter.registerDataSetObserver(observer)

    override fun notifyDataSetChanged() = adapter.notifyDataSetChanged()

    override fun getItemPosition(`object`: Any) = adapter.getItemPosition(`object`)
  }
}