Jon Willis は、彼のコードで無限スクロールを有効にする方法について投稿しています。そこで彼は、Androidサポートライブラリ内でViewPagerクラスにいくつかの変更を加えたと述べました。
Shereefの回答ありがとうございます。
私はそれを少し違った方法で解決しました。
AndroidサポートライブラリのViewPagerクラスのコードを変更しました。メソッドsetCurrentItem(int)
アニメーションでページを変更します。このメソッドは、スムーズなスクロールを可能にするインデックスとフラグを必要とする内部メソッドを呼び出します。このフラグは_boolean smoothScroll
_です。 2番目のパラメーター_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がなければ、自分で解決できなかったでしょう。
編集:ここに blogpost これについて:
アダプターに小さなハックを使用して、この問題を非常に簡単に解決しました。ここに私のコードがあります:
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
既存のアダプタークラスの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);
}
あなたがする必要があるのは、例を見るだけです here
295行目では、ページは常に1に設定されており、スクロール可能であり、getCount()
メソッドのページ数は3です。
これらは変更する必要がある2つの主なものであり、残りはあなたのロジックであり、それらを異なる方法で処理できます。
行295で現在のページを常に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上のカレンダーアプリのあるバージョンはViewSwitcher
を使用しているようです(つまり、表示されるページと非表示のページが2ページしかないことを意味します)。ユーザーがそれに応じて次/前のページをスワイプおよびレンダリングする方法に基づいた遷移アニメーションこの方法では、2つのページを無限に切り替えるだけなので、無限のページネーションを取得します。これには、ページにView
を使用する必要があります最初のアプローチで行った方法です。
一般に、「無限のページネーション」が必要な場合は、おそらくページが何らかの理由で日付または時刻に基づいているためです。その場合は、代わりに比較的無限の時間の有限サブセットを使用することを検討してください。これは、たとえばCalendarView
の実装方法です。または、ViewSwitcher
アプローチを使用できます。これらの2つのアプローチの利点は、ViewSwitcher
またはViewPager
で特に異常なことを行わず、無限に振る舞わせるためのトリックや再実装を必要としないことです(ViewSwitcher
は既にビューを無限に切り替えるように設計されていますが、ViewPager
は、有限ではあるが必ずしも一定ではないページのセットで動作するように設計されています)。
以前のサンプルに基づいた無限スライダーアダプタースケルトン
いくつかの重大な問題:
あなたは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);
}
}
ハッキング by 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>
完了
数日間の無限スクロールの場合、ページャーに適切なフラグメントがあることが重要です。そのため、このページに回答を書きました( Viewpager in Android日を無限に切り替える )
それは非常にうまく機能しています!上記の答えは、私がそれを機能させたかったので、私には機能しませんでした。
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に変更するだけで、変更する必要があるのはそれだけです。
ViewPager、pagerAdapter(またはFragmentStatePagerAdapter)、およびオプションのTabLayoutを無限にスクロールできるライブラリを作成しました。
https://github.com/memorex386/infinite-scroll-viewpager-w-tabs