Jon Willis a posté sur la façon de permettre un défilement infini avec son code . Il y a indiqué qu'il avait apporté quelques modifications à la classe ViewPager dans la bibliothèque de support Android. Quelles modifications ont été apportées et comment est-il possible de "recompiler" la bibliothèque avec la modification ViewPager?
Merci pour votre réponse Shereef.
Je l'ai résolu un peu différemment.
J'ai changé le code de la classe ViewPager de la bibliothèque de support Android. La méthode setCurrentItem(int)
change la page avec animation. Cette méthode appelle une méthode interne qui requiert l'index et un indicateur permettant un défilement régulier. Cet indicateur est boolean smoothScroll
. L'extension de cette méthode avec un deuxième paramètre boolean smoothScroll
l'a résolu pour moi . Appeler cette méthode setCurrentItem(int index, boolean smoothScroll)
m'a permis de le faire défiler indéfiniment.
Voici un exemple complet:
Veuillez considérer que seule la page centrale est affichée ..__ De plus, ai-je rangé les pages séparément, ce qui me permet de les manipuler plus facilement.
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);
}
}
});
Cependant, sans Jon Willis Code, je ne l'aurais pas résolu moi-même.
EDIT: voici un blogpost à ce sujet:
J'ai résolu ce problème très simplement en utilisant un petit bidouillage dans l'adaptateur. Voici mon code:
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;
}
}
}
Et puis, dans ViewPager, nous plaçons la page en cours au milieu:
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
Page infinie en repérant 4 méthodes d’adaptateur dans votre classe d’adaptateurs existante
@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);
}
Il suffit de regarder l'exemple ici
Vous constaterez qu'à la ligne 295, la page est toujours définie sur 1 de sorte qu'elle défile Et que le nombre de pages est de 3 dans la méthode getCount()
.
Ce sont les 2 choses principales que vous devez changer, le reste est votre logique et vous pouvez les gérer différemment.
Créez simplement un compteur personnel qui compte la page réelle sur laquelle vous vous trouvez car la position ne sera plus utilisable après avoir toujours défini la page en cours à 1 sur la ligne 295.
p.s. ce code n'est pas le mien, il a été référencé dans la question que vous avez liée dans votre question
En fait, j’ai étudié les différentes façons de faire cette pagination "infinie", et même si la notion humaine du temps est qu’il est infini (même si nous avons une notion du début et de la fin des temps), les ordinateurs dans le discret. Il y a un temps minimum et maximum (qui peut être ajusté au fil du temps, rappelez-vous la base de la peur de l'an 2000?).
Quoi qu’il en soit, le but de cette discussion est qu’il est/devrait être suffisant pour prendre en charge une plage de dates relativement infinie à travers une plage de dates réellement définie. Un bon exemple de ceci est l'implémentation CalendarView
du framework Android et la WeeksAdapter
qu'il contient. La date minimale par défaut est 1900 et la date maximale par défaut est 2100. Ceci devrait couvrir facilement 99% de l'utilisation du calendrier de toute personne se trouvant dans un rayon de 10 ans autour de la journée.
Ce qu’ils font dans leur implémentation (centrée sur les semaines) est de calculer le nombre de semaines entre la date minimale et maximale. Cela devient le nombre de pages du pager. N'oubliez pas que le pager n'a pas besoin de gérer toutes ces pages simultanément (setOffscreenPageLimit(int)
), il doit simplement pouvoir créer la page en fonction du numéro de page (ou de l'index/de la position). Dans ce cas, l'index est le nombre de semaines de la semaine depuis la date minimum. Avec cette approche, il vous suffit de conserver la date minimale et le nombre de pages (distance par rapport à la date maximale). Ensuite, pour toute page, vous pouvez facilement calculer la semaine associée à cette page. Pas question de danser sur le fait que ViewPager
ne supporte pas la boucle (a.k. une pagination infinie), et d'essayer de le forcer à se comporter comme s'il pouvait défiler à l'infini.
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;
}
};
Alors WeekDaysFragment
peut facilement afficher la semaine à partir de la date passée dans ses arguments.
Alternativement, il semble qu'une version de l'application Agenda sous Android utilise une ViewSwitcher
(ce qui signifie qu'il n'y a que 2 pages, celle que vous voyez et la page cachée). Il modifie ensuite l'animation de la transition en fonction de la manière dont l'utilisateur a balayé et affiche la page suivante/précédente en conséquence. De cette façon, vous obtenez une pagination infinie, car il vous suffit de basculer entre deux pages à l'infini. Cela nécessite l’utilisation de View
pour la page, ce qui est le cas avec la première approche.
En général, si vous voulez une "pagination infinie", c'est probablement parce que vos pages sont basées sur des dates ou des heures. Si tel est le cas, envisagez plutôt d'utiliser un sous-ensemble fini de temps relativement infini. Voici comment CalendarView
est implémenté. Ou vous pouvez utiliser l'approche ViewSwitcher
. L’avantage de ces deux approches est qu’aucune des opérations particulièrement inhabituelles avec ViewSwitcher
ou ViewPager
ne fait rien, et ne nécessite aucune astuce ni aucune réimplémentation pour les forcer à se comporter indéfiniment (ViewSwitcher
est déjà conçu pour basculer infiniment entre les vues, mais ViewPager
est conçu pour travailler sur un ensemble de pages fini, mais pas nécessairement constant).
squelette d'adaptateur de curseur infini basé sur des exemples précédents
quelques problèmes critiques:
vous pouvez regarder le logcat pour comprendre ce qui se passe dans cet exemple
<?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>
Et alors:
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);
}
}
Son piraté par 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>
Terminé
Pour le défilement infini avec les jours, il est important que vous ayez le bon fragment dans le pager, c’est pourquoi j’ai écrit ma réponse sur cette page ( Viewpager dans Android pour basculer entre les jours )
Cela fonctionne très bien! Les réponses ci-dessus n'ont pas fonctionné pour moi car je voulais que cela fonctionne.
Basé sur https://github.com/antonyt/InfiniteViewPager J'ai écrit ceci qui fonctionne bien:
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`)
}
}
Pour le consommer, changez simplement votre ViewPager en InfiniteViewPager et c’est tout ce que vous avez besoin de changer.
J'ai construit une bibliothèque qui peut faire de n'importe quel ViewPager, pagerAdapter (ou FragmentStatePagerAdapter) et facultatif TabLayout Scrolling infiniment.
https://github.com/memorex386/infinite-scroll-viewpager-w-tabs