I would like to set my ViewPager to do circular scrolling. I want the first page to be able to scroll to page 2 AND the last page. And I would like my last page to scroll to
I have created the circular viewpager with smooth scroll from last to first in swipe left and first to last during swipe right.
for this add the last page in the starting and the first page to the last: inside addOnPageChangeListener : we have to do some calculation , when we are 0 position then on onPageScrollStateChanged set the current item as the last item and vise-versa.
Have a look to the code
public class ViewPagerCircular_new extends AppCompatActivity {
ViewPager viewPager;
ArrayList<String> str = new ArrayList<String>();
boolean chageImage = false;
int setPos;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.view_pager_normal);
viewPager = (ViewPager) findViewById(R.id.vf_home_top_pager);
str.add("6"); // added the last page to the frist
str.add("1"); // First item to display in view pager
str.add("2");
str.add("3");
str.add("4");
str.add("5");
str.add("6"); // last item to display in view pager
str.add("1"); // added the first page to the last
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
{
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
if (position == str.size() - 1)
{
chageImage = true;
setPos = 1;
} else if (position == 0)
{
chageImage = true;
setPos = str.size() - 2;
} else
{
chageImage = false;
}
}
@Override
public void onPageSelected(int position)
{
}
@Override
public void onPageScrollStateChanged(int state)
{
if (state == ViewPager.SCROLL_STATE_IDLE && chageImage)
{
viewPager.setCurrentItem(setPos, false);
}
}
});
// display the 1st item as current item
viewPager.setCurrentItem(1);
}
// pager adapter
public class ViewPagerAdapter extends FragmentStatePagerAdapter
{
public ViewPagerAdapter(FragmentManager fm)
{
super(fm);
}
@Override
public Fragment getItem(int position)
{
PagerFragment fragment = new PagerFragment();
Bundle bundle = new Bundle();
bundle.putString("pos", str.get(position));
fragment.setArguments(bundle);
return fragment;
}
@Override
public int getCount()
{
return str.size();
}
}
// fragment to display in adapter
public class PagerFragment extends Fragment
{
Bundle bundle;
String pos;
public PagerFragment()
{
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
bundle = getArguments();
pos = bundle.getString("pos");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
TextView textView = new TextView(ViewPagerCircular_new.this);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(25);
textView.setText(pos);
return textView;
}
}
}
Solved the missing of animation while going from right to left on swiping first page to last and vice-versa
A slight modification in @tobi_b answer,solved my issue. for this add copy of the last page in the starting and the first page to the last.Then just remove the NOT operator from the below method in PagerAdapter.And change setCurrentItem(lastposition,false) to setCurrentItem(lastposition-1,false) and setCurrentItem(0,false) to setCurrentItem(1,false).
private void setNextItemIfNeeded() {
if (isScrollStateSettling()) {
handleSetNextItem();
}
}
private boolean isScrollStateSettling() {
return mScrollState == ViewPager.SCROLL_STATE_SETTLING;
}
private void handleSetNextItem() {
final int lastPosition = pager.getAdapter().getCount() - 1;
if(mCurrentPosition == 0) {
pager.setCurrentItem(lastPosition-1, false);
} else if(mCurrentPosition == lastPosition) {
pager.setCurrentItem(1, false);
}
}
And in your MainActivity.java dont forget to set the current item as 1. Hoping this will help some.
I slightly modified the answer from @portfoliobuilder. It's very simple.
Setting the current item with no smooth scroll until PageChangeState
to be "0", so it would be very smooth.
((ViewPager)container).setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
currentPage = position;
}
@Override
public void onPageScrollStateChanged(int state) {
// state equals to 0 means the scroll action is stop
if(state == 0) {
if(currentPage == 0)
((ViewPager)container).setCurrentItem(imageResourceList.size()-2,false);
if(currentPage == imageResourceList.size()-1)
((ViewPager)container).setCurrentItem(1,false);
}
}
});
its a simple example , important part of listener is on STATE_IDLE detection of cycle end point. so first implement OnPageChangeListener class then on your handler class add this part :
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
if (mPreviousPosition == mCurrentPosition && !mIsEndOfCycle) {
if (mCurrentPosition == 0) {
mViewPager.setCurrentItem(getAdapterItemsCount() - 1);
} else {
mViewPager.setCurrentItem(0);
}
mIsEndOfCycle = true;
} else {
mIsEndOfCycle = false;
}
mPreviousPosition = mCurrentPosition;
}
}
This is my solution:
myViewPager.java
public class myViewPager extends PagerAdapter {
public myViewPager (Context context) {
this.context = context;
}
@Override
public int getCount() {
return rank.length+2;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
// Declare Variables
TextView textViewRank;
//Log.i(TAG, "get position = "+position);
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemView = inflater.inflate(R.layout.viewpager_item, container,
false);
textViewRank= (TextView) itemView.findViewById(R.id.textView1);
if (position == getCount() - 1) {
textViewRank.setText(rank[0]);
} else if (position == 0) {
textViewRank.setText(rank[rank.length-1]);
} else {
textViewRank.setText(rank[position-1]);
}
((ViewPager) container).addView(itemView);
return itemView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
viewpager_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/textView1"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getName();
public static String[] rank;
myViewPager adapter;
private ViewPager viewPager;
private static int currentPage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rank = new String[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
viewPager = (ViewPager) findViewById(R.id.view_pager);
adapter = new myViewPager(this);
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Log.i(TAG, "onPageSelected = "+position);
currentPage = position;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// not needed
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
int pageCount = rank.length+2;
if (currentPage == pageCount-1){
viewPager.setCurrentItem(1,false);
} else if (currentPage == 0){
viewPager.setCurrentItem(pageCount-2,false);
}
}
}
});
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
Assumed that the array has 10 elements. Then "add" 2 dummy elements, but not really add. When "get_count" function is called, just return size=10+2 elements.
dummy e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9] dummy
In viewPager.addOnPageChangeListener:
when you slide to the last element, viewPager should set the first element as next.
if (currentPage == pageCount-1){
viewPager.setCurrentItem(1,false);
}
when you slide to the first element, viewPager should set the last element as next.
else if (currentPage == 0){
viewPager.setCurrentItem(pageCount-2,false);
}
In instantiateItem :
if (position == getCount() - 1) {
textViewRank.setText(rank[0]);
} else if (position == 0) {
textViewRank.setText(rank[rank.length-1]);
} else {
textViewRank.setText(rank[position-1]);
}
Ok, I have an answer. It was actually easier than I expected, but it does take some trickery. First, let me begin with the set up. Lets say, for example, you have three pages (A-B-C) that you are scrolling through in your ViewPager. And you want to set it up so that if you continue scrolling on C (pg. 3), it goes to A (pg. 1) and if you scrolled backwards on A (pg. 1) it goes to C (pg. 3).
I am not saying my solution is the best, but it works and I do not see any issues. First, you have to create two "fake" pages. The fake pages represent the first and last pages of your ViewPager. The next thing you will need is to set up an onPageChangeListener(), and use the method onPageSelected(). The reason why you need the fake pages is because onPageSelected() only registers after you have moved (swiped). In other words, without this method the end user would have to scroll to page 2 and back to page 1 to receive a hit on page 1, which also means that page 1 would be skipped depending on your code logic.
The setup is really the entire answer. Once you have your fake pages, it is just a matter of using setCurrentItem() inside the necessary method.
Here is how my code looks. Be sure to place this inside your public Object instantiateItem(final ViewGroup container, final int position) method, just before you return your view inside of your container.
((ViewPager) container).setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Log.d(TAG, "onPageSelected() :: " + "position: " + position);
// skip fake page (first), go to last page
if (position == 0) {
((ViewPager) container).setCurrentItem(118, false);
}
// skip fake page (last), go to first page
if (position == 119) {
((ViewPager) container).setCurrentItem(1, false); //notice how this jumps to position 1, and not position 0. Position 0 is the fake page!
}
}
That's it, it does the trick! The only other thing to do is start your ViewPager on position 1 (which is the second page: fake page = pg 1, my real starting page = pg 2). Now, every time I scroll to the fake page, I redirect it backwards to the last real page. And every time I scroll forward to the last fake page, I redirect it forwards to the real starting page (pg 2).
Also, do not try to put any code in onPageScrollStateChanged. That method is bizarre, it seems that the state value is uncontrollable. It constantly jumps from one state to another. Even without scrolling. That is just a tip I picked up.