Бесконечная прокрутка изображения ViewPager

Как задокументировано Google, класс Gallery устарел на уровне API 16. Этот виджет больше не поддерживается. Другие виджеты с горизонтальной прокруткой включают HorizontalScrollView и ViewPager из библиотеки поддержки. Поэтому я использовал ViewPager как альтернативу классу Gallery.

Моя цель, наконец, добиться бесконечно прокручиваемого изображения ViewPager с текстовыми описаниями. Я использовал приведенный ниже код для получения изображения ViewPager с текстом, описывающим каждое изображение, но как мне применить бесконечную прокрутку к ViewPager?

Я раньше не работал с ViewPager, поэтому, если возможно, постарайтесь предоставить подробный код.

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
       android:layout_width="fill_parent" 
       android:layout_height="fill_parent" 
       android:orientation="vertical">
  <android.support.v4.view.ViewPager 
       android:id="@+id/myimagepager" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" /> 
</LinearLayout>

custom_pager.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
   android:layout_width="match_parent" 
   android:layout_height="match_parent" 
   android:orientation="vertical"  
   android:gravity="center_horizontal">
   <ImageView 
       android:id="@+id/myimage" 
       android:layout_width="match_parent" 
       android:layout_height="0dp" 
       android:layout_margin="5dp" 
       android:layout_weight="2" /> 
    <TextView 
       android:id="@+id/image_text" 
       android:layout_width="fill_parent" 
       android:layout_height="0dp"   
       android:layout_weight="1"/>

</LinearLayout>

ImagePager:

public class ImagePager extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImagePagerAdapter adapter = new ImagePagerAdapter(this, imageArra, stringArray );
        ViewPager myPager = (ViewPager) findViewById(R.id.myimagepager);
        myPager.setAdapter(adapter);
        myPager.setCurrentItem(0);
    }

    private int imageArra[] = { R.drawable.a, R.drawable.b,R.drawable.c, 
                                 R.drawable.d,R.drawable.e,R.drawable.f,
                                 R.drawable.g, R.drawable.h, R.drawable.i};

    private String[] stringArray = new String[] { "Image a", "Image b","Image c"
                                                   "Image d","Image e","Image f", 
                                                   "Image g","Image h","Image i"};


}

ImagePagerAdapter:

public class ImagePagerAdapter extends PagerAdapter {

    Activity activity;
    int imageArray[];
    String[] stringArray;

    public ImagePagerAdapter(Activity act, int[] imgArra, String[] stringArra) {
        imageArray = imgArra;
        activity = act;
        stringArray = stringArra;
    }

    public int getCount() {
        return imageArray.length;
    }

    public Object instantiateItem(View collection, int position) {
        LayoutInflater inflater = (LayoutInflater)collection.getContext
                          ().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View layout = inflater.inflate(R.layout.custom_pager, null);   

        ImageView im=(ImageView) layout.findViewById(R.id.myimage);             
        im.setImageResource(imageArray[position]);

        TextView txt=(TextView) layout.findViewById(R.id.image_text);
        txt.setText(stringArray[position]);

        ((ViewPager) collection).addView(layout, 0);
          return layout;   
    }

    @Override
    public void destroyItem(View arg0, int arg1, Object arg2) {
        ((ViewPager) arg0).removeView((View) arg2);
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == ((View) arg1);
    }

    @Override
    public Parcelable saveState() {
        return null; 
    }


}

person Android Stack    schedule 02.12.2012    source источник
comment
Класс галереи устарел .. после того, как я много гуглил и искал, я заканчиваю на ViewPager Ха. Мне потребовалось всего несколько секунд, чтобы ввести «галерея abdroid» (да, я ошибся в написании) в Google, чтобы оказаться в документах для Gallery. В первых трех предложениях указано... Этот класс устарел на уровне API 16. Этот виджет больше не поддерживается. Другие виджеты с горизонтальной прокруткой включают HorizontalScrollView и ViewPager из библиотеки поддержки. Совет на будущее. Прежде всего, прочитайте документы. ;)   -  person Andrew Thompson    schedule 02.12.2012
comment
@Andrew Thompson, мой дорогой пост обновлен, и я уже видел этот документ из Google, это было просто для того, чтобы объяснить, почему я перешел на viewpager, спасибо.   -  person Android Stack    schedule 02.12.2012


Ответы (7)


У меня была такая же проблема, но я смог найти способ решить это — код можно найти на github.

Если вы скопируете классы InfiniteViewPager и InfinitePagerAdapter в свой проект, вы можете добиться бесконечной (свернутой) прокрутки с небольшими изменениями.

В своей деятельности оберните свой PagerAdapter InfinitePagerAdapter:

PagerAdapter adapter = new InfinitePagerAdapter(new ImagePagerAdapter(this, imageArra, stringArray));

Измените ViewPager в XML активности на InfiniteViewPager:

<com.antonyt.infiniteviewpager.InfiniteViewPager 
       android:id="@+id/myimagepager" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" />

Вы можете переименовать классы как хотите. Этот код работает только в том случае, если у вас есть как минимум три страницы (в вашем примере кода девять, так что для этого он подойдет).

person antonyt    schedule 09.12.2012
comment
это работает, но, пожалуйста, у меня есть вопрос, теперь у меня есть 4 класса в моем проекте: 1-ImagePager, 2-ImagePagerAdapter, 3-InfinitePagerAdapter, 4-InfiniteViewPager.java. можем ли мы объединить их только в один класс или два класса, этот бесконечный пейджер изображений будет частью большого проекта, или мы должны сохранить его, поскольку он состоит из 4 классов, и повлияет ли это на производительность или нет, спасибо - person Android Stack; 10.12.2012
comment
Я не думаю, что вы обнаружите какую-либо заметную разницу в производительности при использовании 4 классов против 2 классов. Лучше хранить их отдельно, потому что тогда вы можете добавить поведение бесконечной прокрутки к любому PagerAdapter/ViewPager, который вы напишете позже, - объединение их всех вместе будет означать, что его нельзя легко использовать повторно в другом контексте. - person antonyt; 10.12.2012
comment
Хорошо, я понимаю, еще одно: 1- почему у нас нет одного адаптера только в этом проекте, потому что вы упоминаете :((оберните свой PagerAdapter с помощью InfinitePagerAdapter)), почему мы не написали один адаптер и назвали его: InfinitePagerAdapter, или там это определенное поведение должно быть получено путем обертывания процесса, 2-вы упоминаете в своем коде: разрешить 100 циклов назад с самого начала, должно быть достаточно, чтобы создать иллюзию бесконечности. это означает, что у меня есть новые 100 циклов каждый раз, когда я открываю свое приложение, не могли бы вы уточнить эти два момента, пожалуйста, спасибо - person Android Stack; 10.12.2012
comment
У вашего ImagePagerAdapter есть определенная работа — он обслуживает макет для каждой страницы и не должен беспокоиться ни о чем другом. InfinitePagerAdapter также заботится только о бесконечной прокрутке, а не о конкретной обслуживаемой странице - он не загружает никаких собственных представлений. Это просто конструктивный выбор, чтобы разделить их таким образом, который позволяет InfinitePagerAdapter работать с ЛЮБЫМ написанным вами PagerAdapter. 100 циклов — это время жизни View, которое обычно совпадает со временем жизни Activity. Повторное создание действия приведет к его сбросу. - person antonyt; 10.12.2012
comment
Возвращает ли он правильную правильную выбранную позицию при нажатии изображения? - person RobinHood; 16.12.2013
comment
Как добавить индикатор страницы? - person Aditya; 20.02.2019

Я думаю, что мое решение проще.

Внимание на структуру моего массива изображений:

Item 0          => last image

Item count()-1  => first image

Трюк на onPageScrollStateChanged:

Когда пользователь прокручивает до последнего элемента -> пейджер переходит без анимации к первому изображению (позиция = 1)

Когда пользователь прокручивает до первого элемента -> пейджер переходит без анимации к последнему изображению (позиция = количество - 2)

public class InfiniteScrollingActivity extends ActionBarActivity {

    private ViewPager     pager;
    private MyAdapter     adapter;
    int[] promoImageIds = new int[]{R.drawable.cover6, R.drawable.cover1, R.drawable.cover2, R.drawable.cover3, R.drawable.cover4, R.drawable.cover5, R.drawable.cover6, R.drawable.cover1 };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_test);

        adapter = new MyAdapter(getSupportFragmentManager(), promoImageIds);
        pager = (ViewPager)findViewById(R.id.pager);
        pager.setAdapter(adapter);
        pager.setCurrentItem( 1 );
        pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageSelected(int index) {
                Log.v( "onPageSelected", String.valueOf( index ) );
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
                // Log.v("onPageScrolled", "");
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                Log.v("onPageScrollStateChanged", String.valueOf(state));

                if (state ==ViewPager.SCROLL_STATE_IDLE) {
                    int index = pager.getCurrentItem();
                    if ( index == 0 )
                        pager.setCurrentItem( adapter.getCount() - 2, false );
                    else if ( index == adapter.getCount() - 1 )
                        pager.setCurrentItem( 1 , false);
                }
            }
        });
    }


    public static class MyAdapter extends FragmentPagerAdapter {

        int[] promoImageIds;

        public MyAdapter(FragmentManager fm, int[] promoImageIds){
            super(fm);
            this.promoImageIds = promoImageIds;
        }

        @Override
        public int getCount(){
            return promoImageIds.length;
        }

        @Override
        public Fragment getItem(int position) {

            return PromoFragment.newInstance( promoImageIds[position] );
        }
    }

    public static class PromoFragment extends Fragment
    {
        int imageID;


        static PromoFragment newInstance( int imageID)
        {
            PromoFragment f = new PromoFragment();

            // Supply num input as an argument.
            Bundle args = new Bundle();
            args.putInt( "imageID", imageID );
            f.setArguments(args);

            return f;
        }

        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            imageID = getArguments() != null ? getArguments().getInt( "imageID" ) : null;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState)
        {
            ImageView v = (ImageView) inflater.inflate(R.layout.fragment_image, container, false);
            v.setImageResource( imageID );
            return v;
        }
    }
}
person Shlomi Hasin    schedule 02.09.2014
comment
Сложность заключалась в том, чтобы внимательно прочитать ваш массив promoImageIds и помнить, что вы повторяете последний и первый элементы в своем массиве. Легко реализовать и работает как шарм! - person dianakarenms; 30.05.2017
comment
Этот подход работал лучше всего для меня, он не страдает от проблем с производительностью, которые делает подход Int32.MaxValue Count. - person Justin; 09.11.2018
comment
Работает как шарм, но приведенный ниже индикатор не работает должным образом в списке, у меня есть 3 размера данных, его прокрутка бесконечна, но индикатор показывает 2, вы можете решить эту проблему? - person MustafaShaikh; 16.09.2019

я уже нашел способ сделать простой трюк, надеюсь, он вам поможет

import java.util.ArrayList;
import android.os.Bundle;
import android.app.Activity;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;

public class ImagePager extends Activity {
    String[] stringArray;
    int[] imageArra;
    ViewPager myPager;
    Boolean isScrooled = false;
    // use this array yo understand swipe left or right ?
    ArrayList<Float> pos = new ArrayList<Float>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

            // put  empty view at the beginnig and to end

        imageArra = new int[] { 0, R.drawable.ic_launcher,
                R.drawable.ic_launcher, R.drawable.ic_launcher,
                R.drawable.ic_launcher, R.drawable.ic_launcher,
                R.drawable.ic_launcher, R.drawable.ic_launcher,
                R.drawable.ic_launcher, R.drawable.ic_launcher, 0 };

           // put  empty string at the beginnig and to end

        stringArray = new String[] { "", "Image a", "Image b", "Image c",
                "Image d", "Image e", "Image f", "Image g", "Image h",
                "Image i", "" };

        ImagePagerAdapter adapter = new ImagePagerAdapter(this, imageArra,
                stringArray);
        myPager = (ViewPager) findViewById(R.id.myimagepager);
        myPager.setAdapter(adapter);
        myPager.setCurrentItem(1);

        myPager.setOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int arg0) {
                Log.v("onPageSelected", String.valueOf(arg0));
                pos.clear();
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
                try {
                            // while scrolling i add ever pos to array
                    pos.add(arg1);
                                    // if pos.get(0) > pos.get(pos.size() - 1) 
                    // <----- swipe <-----  
                                    // we should check isScroll because setCurrent item is not a croll ? 

                    if (pos.size() > 0)
                        if (arg0 == imageArra.length - 1
                                & pos.get(0) > pos.get(pos.size() - 1)
                                & isScrooled == true) {
                            try {
                                isScrooled = false;
                                myPager.setCurrentItem(1, false);
                            } catch (Exception e) {
                                Log.v("hata",
                                        "<----- swipe <-----  " + e.toString());
                            }

                        }
                        // ----> swipe ---->
                        else if (arg0 == 0
                                & pos.get(0) < pos.get(pos.size() - 1)
                                & isScrooled == true) {
                            try {
                                isScrooled = false;
                                myPager.setCurrentItem(imageArra.length - 1,
                                        false);
                            } catch (Exception e) {
                                Log.v("hata",
                                        "----> swipe ---->  " + e.toString());
                            }

                        } else if (arg0 == 0 & pos.size() == 1
                                & isScrooled == true) {
                            try {
                                isScrooled = false;
                                myPager.setCurrentItem(imageArra.length - 1,
                                        false);
                            } catch (Exception e) {
                                Log.v("hata",
                                        "----> swipe ---->  " + e.toString());
                            }

                        }

                } catch (Exception e) {
                    Log.v("hata", e.toString());
                }

            }

            @Override
            public void onPageScrollStateChanged(int arg0) {
                Log.v("onPageScrollStateChanged", String.valueOf(arg0));
                            // set is scrolling
                isScrooled = true;
            }
        });

    }

}

[РЕДАКТИРОВАТЬ 1]

import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;

public class ImagePager extends Activity {
    String[] stringArray;
    int[] imageArra;
    ViewPager myPager;
    int scrollState;
    Boolean isFirstVisitEnd= true,isFirstVisitBegin = true;
    ArrayList<Integer> pos = new ArrayList<Integer>();


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageArra = new int[] { 0,R.drawable.ic_launcher,
                R.drawable.ic_launcher, R.drawable.ic_launcher,
                R.drawable.ic_launcher, R.drawable.ic_launcher,
                R.drawable.ic_launcher, R.drawable.ic_launcher,
                R.drawable.ic_launcher, R.drawable.ic_launcher, 0 };

        stringArray = new String[] {"","Image a", "Image b", "Image c",
                "Image d", "Image e", "Image f", "Image g", "Image h",
                "Image i", "" };

        ImagePagerAdapter adapter = new ImagePagerAdapter(this, imageArra,
                stringArray);
        myPager = (ViewPager) findViewById(R.id.myimagepager);
        myPager.setAdapter(adapter);
        myPager.setCurrentItem(1);

        myPager.setOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageSelected(int arg0) {
                Log.v("onPageSelected", String.valueOf(arg0));
                pos.clear();
                if (arg0 == imageArra.length - 1)
                    isFirstVisitEnd = false;
                else
                    isFirstVisitEnd = true;


                if (arg0 == 0)
                    isFirstVisitBegin = false;
                else
                    isFirstVisitBegin = true;
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {                                
                try { 
                    pos.add(Integer.valueOf(arg2));

                    if (pos.size() > 0) {
                        //Log.v("onPageScrolled_arg2","arg0  : "+String.valueOf(arg0)+"   ilk : "+pos.get(0).toString()+"    son : " +pos.get(pos.size() - 1).toString()+ "   "+ String.valueOf(pos.get(0)-(pos.get(pos.size() - 1)))+"    isFirstVisitEnd: "+String.valueOf(isFirstVisitEnd.booleanValue()) );

                        // <----- swipe <-----
                        if (arg0 == imageArra.length - 2& (pos.get(pos.size() - 1) -pos.get(0)  < 100)& scrollState == 2 & isFirstVisitEnd == false) {                          
                            myPager.setCurrentItem(1, false);
                        }

                        //Log.v("onPageScrolled_arg2","arg0  : "+String.valueOf(arg0)+"   ilk : "+pos.get(0).toString()+"    son : " +pos.get(pos.size() - 1).toString()+ "   "+ String.valueOf(pos.get(0)-(pos.get(pos.size() - 1)))+"    isFirstVisitbegin: "+String.valueOf(isFirstVisitBegin.booleanValue()) );
                        if (arg0 == 0 & (pos.get(pos.size() - 1) -pos.get(0)  > -100)& scrollState == 2 & isFirstVisitBegin == false) {                         
                            myPager.setCurrentItem(imageArra.length - 2, false);
                        }       
                    }

                } catch (Exception e) {
                    Log.v("hata", e.toString());
                }

            }

            @Override
            public void onPageScrollStateChanged(int arg0) {
                Log.v("onPageScrollStateChanged", String.valueOf(arg0));
                scrollState =arg0;
            }
        });
    }
}
person Talha    schedule 08.12.2012
comment
хорошо, его прокрутка непрерывна, но все же одна вещь: пустое представление все еще появляется, когда доходит до последнего представления, и вы хотите перейти к первому, как мы преодолеваем это, спасибо - person Android Stack; 09.12.2012
comment
Привет, трудно понять точное значение позиции пейджера, но я изменил код, и я думаю, что теперь он лучше. Пожалуйста, посмотрите на редактирование, и если у вас есть идея улучшить его качество, поделитесь со мной ([email protected]). Я планирую разработать бесконечный пейджер и разместить его на github. - person Talha; 09.12.2012
comment
первый код лучше второго, но все равно оба показывают пустой вид. второй, когда прокручивается, чтобы медленно достичь пустого вида, он складывается на нем, вам нужно прокрутить один вид назад, а затем снова перейти вперед, чтобы продолжить прокрутку, спасибо - person Android Stack; 10.12.2012

Я нашел другое решение, основанное на Шломи Хасине и antonyt отвечает, не изменяя коллекцию.

ViewPager yourPager;
PagerAdapter yourAdapter;
//....
EndlessPagerAdapter endlessPagerAdapter = new EndlessPagerAdapter(yourAdapter, yourPager);
yourPager.setAdapter(endlessPagerAdapter);
yourPager.setCurrentItem(1);//for correct first page

Полный класс:

public class EndlessPagerAdapter extends PagerAdapter {

private PagerAdapter adapter;

public EndlessPagerAdapter(PagerAdapter adapter, ViewPager viewPager) {
    this.adapter = adapter;
    viewPager.addOnPageChangeListener(new SwapPageListener(viewPager));
}

@Override
public int getCount() {
    return adapter.getCount() + 2;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (adapter.getCount() < 2) {
        adapter.instantiateItem(container, position);
    }

    int newPosition;
    if (position == 0) {
        newPosition = adapter.getCount() - 1;
    } else if (position >= getCount() - 1) {
        newPosition = 0;
    } else {
        newPosition = position - 1;
    }
    return adapter.instantiateItem(container, newPosition);
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    adapter.destroyItem(container, position, object);
}

@Override
public void finishUpdate(ViewGroup container) {
    adapter.finishUpdate(container);
}

@Override
public boolean isViewFromObject(View view, Object object) {
    return adapter.isViewFromObject(view, object);
}

@Override
public void restoreState(Parcelable bundle, ClassLoader classLoader) {
    adapter.restoreState(bundle, classLoader);
}

@Override
public Parcelable saveState() {
    return adapter.saveState();
}

@Override
public void startUpdate(ViewGroup container) {
    adapter.startUpdate(container);
}

@Override
public CharSequence getPageTitle(int position) {
    return adapter.getPageTitle(position);
}

@Override
public float getPageWidth(int position) {
    return adapter.getPageWidth(position);
}

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    adapter.setPrimaryItem(container, position, object);
}

@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
    adapter.unregisterDataSetObserver(observer);
}

@Override
public void registerDataSetObserver(DataSetObserver observer) {
    adapter.registerDataSetObserver(observer);
}

@Override
public void notifyDataSetChanged() {
    adapter.notifyDataSetChanged();
}

@Override
public int getItemPosition(Object object) {
    return adapter.getItemPosition(object);
}

private class SwapPageListener implements ViewPager.OnPageChangeListener {

    private ViewPager viewPager;

    SwapPageListener(ViewPager viewPager) {
        this.viewPager = viewPager;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            PagerAdapter pagerAdapter = viewPager.getAdapter();
            if (pagerAdapter != null) {
                int itemCount = pagerAdapter.getCount();
                if (itemCount < 2) {
                    return;
                }
                int index = viewPager.getCurrentItem();
                if (index == 0 ) {
                    viewPager.setCurrentItem(itemCount - 2, false);
                } else if (index == itemCount - 1) {
                    viewPager.setCurrentItem(1, false);
                }
            }
        }
    }
}}

Как это работает:

У нас есть коллекция

коллекция

затем добавьте к нему два дополнительных элемента

новый размер

показать первый элемент в последней позиции и последний элемент в первой позиции

показать элементы

добавить слушателя и поменять местами страницы по событию, с 4 на 1 и с 0 на 3

поменять элементы местами

это все.

Я бы не советовал применять там анимацию или тяжелые макеты, может лагать при перестановке элементов. (возможно, лагов не встречал)

Также не устанавливайте этот адаптер в пейджер более одного раза или перемещайте SwapPageListener во внешний класс и устанавливайте его в блоке инициализации.

person Anrimian    schedule 16.10.2017

Для бесконечной прокрутки с днями важно, чтобы у вас был хороший фрагмент в пейджере, поэтому я написал свой ответ об этом на странице (Viewpager в Android для бесконечного переключения между днями)

Это работает очень хорошо! Приведенные выше ответы не сработали для меня, так как я хотел, чтобы это сработало.

person Richard Lindhout    schedule 01.05.2014

RecyclerViewPager имеет реализацию infinite scrolling и может прокручиваться как галерея.

person panwy    schedule 30.10.2015

person    schedule
comment
Объясните и сделайте отступ! - person Hemant Kaushik; 06.11.2018