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

Jon Willis已经发布了如何使用他的代码无限滚动。 在那里他说,他在Android支持库中的ViewPager类中做了一些改变。 已经做了哪些更改以及如何使用ViewPager更改来重新编译库?

我很简单地在适配器中使用一些小技巧来解决这个问题。 这是我的代码:

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 

谢谢你的回答Shereef。

我解决了一点点不同。

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

用animation改变页面。 这个方法调用一个内部的方法,需要索引和一个标志使平滑滚动。 这个标志是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); } } }); 

但是,没有Jon Willis Code,我自己也不会解决它。

编辑:这是一个关于这个博客 :

通过在现有的适配器类中重写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); } 

所有你需要做的就是看这里的例子

您会发现在第295行中,页面始终设置为1,因此它是可滚动的, getCount()方法中的页数是3。

这些是你需要改变的两个主要的东西,其余的是你的逻辑,你可以以不同的方式处理它们。

只要build立一个个人计数器来统计你所在的实际页面,因为在295行总是将当前页面设置为1之后,位置将不再可用。

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

其实,我一直在想办法做这个“无限”的分页,即使人类的时间观念是无限的(即使我们有时间的开始和结束的概念),计算机也是如此在离散。 有一个最小和最大时间(可以随着时间的推移进行调整,请记住千年虫恐慌的基础?)。

无论如何,这个讨论的要点是,它应该足以支持相对无限的date范围,通过实际有限的date范围。 一个很好的例子就是Android框架的CalendarView实现,以及其中的WeeksAdapter 。 默认的最短date是1900年,默认的最大date是2100年,这应该覆盖今天十年半径范围内任何人的99%的日历使用。

他们在实施过程中所做的工作(集中在几个星期)是计算最小和最大date之间的星期数。 这成为寻呼机中的页面数量。 请记住,寻呼机不需要同时维护所有这些页面( setOffscreenPageLimit(int) ),它只需要能够基于页码(或索引/位置)创build页面。 在这种情况下,指数是一周从最低date开始的周数。 使用这种方法,您只需保持最短date和页数(到最大date的距离),那么对于任何页面,您都可以轻松计算与该页面关联的星期。 没有围绕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可以很容易地显示从参数传入的date开始的一周。

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

一般来说,如果你想要“无限的分页”,这可能是因为你的页面基于date或时间。 如果是这种情况,请考虑使用相对无限的时间有限的子集。 这就是CalendarView如何实现的例子。 或者你可以使用ViewSwitcher方法。 这两种方法的优点是ViewSwitcherViewPager都没有特别的exception,并且不需要任何技巧或重新实现来强制它们无限的运行( ViewSwitcher已经被devise为无限地切换视图,但是ViewPager被devise为在一个有限的,但不一定是一组页面上工作)。

基于前面的示例的无限滑块适配器骨架

一些关键问题:

  • 记得页面视图中的原始(相对)位置(样本中使用的标记),所以我们将看这个位置来定义视图的相对位置。 否则在寻呼机中的子命令是混合的
  • 必须先填充适配器内的绝对视图。 (这个填充的其余时间将是无效的)发现没有办法强制它从寻呼机处理程序填充。 其余时间的绝对视图将被正确值的pager处理程序覆盖。
  • 当页面快速滑动时,页面处理程序不会填充页面(实际上是左侧)。 暂时没有解决方法,只是使用空视图,当停止拖动时,它将被填充实际值。 upd:快速解决方法:禁用适配器的destroyItem。

你可以看看logcat来了解这个示例中发生了什么

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

CustomPagerAdapter 破解

MainActivity.java

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

activity_main.xml

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

row_item_viewpager.xml

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/txtItem" android:textAppearance="@android:style/TextAppearance.Large"/> </LinearLayout> 

完成

对于天数无限的滚动,重要的是你在寻呼机中有好的片段,因此我在页面上写了我的答案( Android中的Viewpager在无休止的几天之间切换 )

它工作得很好! 以上的答案不适合我,因为我想要它的工作。

我build立了一个库,可以使任何ViewPager,pagerAdapter(或FragmentStatePagerAdapter)和可选的TabLayout无限滚动。

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