46

Jon Willis发布了关于如何使用他的代码启用无限滚动的帖子。

在那里,他说他ViewPager在 android 支持库中对类进行了一些更改。进行了哪些更改,如何通过更改“重新编译”库ViewPager

4

10 回答 10

40

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

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
于 2012-03-04T14:59:47.200 回答
38

谢谢你的回答谢里夫。

我以不同的方式解决了它。

我更改了android支持库的ViewPager类的代码。方法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);
            }
        }
    });

但是,如果没有 Jon Willis Code,我自己就无法解决它。

编辑:这是一篇关于此的博文:

于 2011-12-01T21:41:42.077 回答
32

通过覆盖现有适配器类中的 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);
    }
于 2013-07-02T11:34:52.413 回答
10

实际上,我一直在研究各种方法来进行这种“无限”分页,尽管人类对时间的概念是无限的(即使我们有时间的开始和结束的概念),但计算机仍然可以处理在离散。有一个最小和最大时间(可以随着时间的推移进行调整,还记得 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 上的某些版本的日历应用程序似乎使用 a ViewSwitcher(这意味着只有 2 个页面,您看到的页面和隐藏页面)。然后它会根据用户滑动的方式更改过渡动画,并相应地呈现下一个/上一个页面。通过这种方式,您可以获得无限分页,因为它只是在两个页面之间无限切换。不过,这需要对页面使用 a View,这是我采用第一种方法的方式。

一般来说,如果您想要“无限分页”,可能是因为您的页面以某种方式基于日期或时间。如果是这种情况,请考虑使用相对无限的有限时间子集。例如,这是如何CalendarView实现的。或者您可以使用该ViewSwitcher方法。这两种方法的优点是 or 没有什么特别的不寻常之处ViewSwitcherViewPager并且不需要任何技巧或重新实现来强制它们无限地运行(ViewSwitcher已经被设计为在视图之间无限地切换,但ViewPager被设计为在有限的,但不一定是恒定的页面集)。

于 2012-12-26T22:42:00.627 回答
9

您需要做的就是查看此处的示例

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

这些是您需要更改的两件主要事情,其余的是您的逻辑,您可以以不同的方式处理它们。

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

ps此代码不是我的,它在您在问题中链接的问题中被引用

于 2011-11-23T10:16:53.950 回答
3

基于先前样本的无限滑块适配器骨架

一些关键问题:

  • 记住页面视图中的原始(相对)位置(示例中使用的标签),所以我们将查看这个位置来定义视图的相对位置。否则寻呼机中的子订单是混合的
  • 必须在适配器内填充第一次绝对视图。(其余时间此填充将无效)找不到强制从寻呼机处理程序填充它的方法。休息时间绝对视图将从具有正确值的寻呼机处理程序中覆盖。
  • 当页面快速滑动时,页面处理程序不会填充侧页(实际上是左侧)。暂时没有解决方法,只使用空视图,当拖动停止时它将填充实际值。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);
    }
}
于 2014-02-08T01:06:19.787 回答
2

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

    }
}

活动主.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>

完毕

于 2015-12-03T08:09:23.133 回答
0

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

它工作得很好!以上答案对我不起作用,因为我希望它起作用。

于 2014-05-01T15:32:30.637 回答
0

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

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

于 2017-06-15T19:38:19.940 回答
0

基于https://github.com/antonyt/InfiniteViewPager我写了这个效果很好:

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

要使用它,只需将 ViewPager 更改为 InfiniteViewPager,这就是您需要更改的全部内容。

于 2018-12-26T16:44:09.030 回答