8

I'm developing an app and currently I am trying to add ScreenSlide to it. I managed to do this using a tutorial, but usual sliding between x pages is not quite what I'm looking for.

With code provided below I can slide between 5 pages, but pages aligned in a straight line and you can't go from first page straight to 5th and vise versa. In my app I have 4 pages. When I slide left I switch between first 2 pages, when I slide right I switch between 2 last pages. On image below you can see how my current code switches page and under it - my goal.

enter image description here

    public class MainActivity extends FragmentActivity {
    private static final int NUM_PAGES = 5;


    private ViewPager mPager;
    private ScreenSlidePageFragment[] pages = new ScreenSlidePageFragment[NUM_PAGES];


    private PagerAdapter mPagerAdapter;

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

        mPager = (ViewPager) findViewById(R.id.pager);
        mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);

    }

    @Override
    public void onBackPressed() {
        if (mPager.getCurrentItem() == 0) {
            super.onBackPressed();
        } else {
            mPager.setCurrentItem(mPager.getCurrentItem() - 1);
        } 
    }

    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }



        @Override
        public int getCount() {
            return NUM_PAGES;
        }

        @Override
        public Fragment getItem(int position) {
            boolean moveRight = mPager.getCurrentItem() < position;
            boolean moveLeft = mPager.getCurrentItem() > position;
            switch(position){
                case 5: 
                    if(moveRight){

                        return geLog.w("i"
                            + "Info", Integer.toString(position));
                       //return getPageByPosition(2);
                    if(moveLeft)
                        return getPageByPosition(2);


            }
            return new ScreenSlidePageFragment();
        }

        private Fragment getPageByPosition(int position){
            int index = position - 1;
            if(index < 0 || index > NUM_PAGES-1)
                throw new InvalidParameterException("requested position is invalid");

            if(pages[index] == null)
                pages[index] = new ScreenSlidePageFragment();
            return pages[index]; 
        }

    }


}

[UPDATE]

I've managed to write a code that allows me to infinitely slide to the right between 6 different pages. Left side is limited though - I can slide only to the first page(so if I'm on 1st page after I cycled 3 times to the right, I can make only 3 cycles backwards). I think I am very close to finding the solution. Any ideas?

MainActivity.java

    public class MainActivity extends FragmentActivity  {

private static final int NUM_PAGES = 6;
private ViewPager pager;
private PagerAdapter pagerAdapter;
private List<ScreenSlidePageFragment> slideList;

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

    slideList = new ArrayList<ScreenSlidePageFragment>();
    for (int i = 0; i<NUM_PAGES; i++){
        ScreenSlidePageFragment slide = new ScreenSlidePageFragment();
        slide.setIndex(i+1);
        slideList.add(slide);
    }

    pager = (ViewPager) findViewById(R.id.pager);
    pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
    pager.setAdapter(pagerAdapter);
}

private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {

    public ScreenSlidePagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        int _pos = position % NUM_PAGES;
        return slideList.get(_pos);
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }
}
}

ScreenSlidePageFragment.java

  public class ScreenSlidePageFragment extends Fragment {


        private int index;

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            ViewGroup root = (ViewGroup)inflater.inflate(R.layout.slide, container, false);
            TextView tw = (TextView) root.findViewById(R.id.textView);
            tw.setText(Integer.toString(index));
            return root;
        }

        public void setIndex(int index){
            this.index = index;
        }
        }
C0D3LIC1OU5
  • 8,600
  • 2
  • 37
  • 47
Oleksandr Firsov
  • 1,428
  • 1
  • 21
  • 48
  • This is such a confusing flow in terms of page permanence and user experience that I highly recommend you reconsider your navigation design. That being said you'll likely need to do the animation and gesture recognition yourself instead of using the pager adapter. Otherwise you will need to modify the pager adapter after each page change. http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html – Dave S Sep 29 '15 at 19:26
  • I gave you +1 for the drawing :-) – Christine Sep 29 '15 at 20:34
  • override `onBackPressed()` in each fragment; something like this perhaps? `onBackPressed() { getViewPager().setSelectedItem(0); }` – Shark Oct 02 '15 at 15:18
  • isnt this what you are looking for? https://github.com/antonyt/InfiniteViewPager – Nanoc Oct 06 '15 at 10:36

4 Answers4

3

One possible workaround is not using viewpager, but creating your own custom Slider, I ended up with this solution, since ViewPager is very unflexible, like for example if you try to do something like Facebook Page app kind of thing. Cons: you have to manage all the touches and lifecycle.

Another simpler solution for this is basically use the following API if I understand the question correctly it should be enough. ViewPager.setCurrentItem

Iliiaz Akhmedov
  • 867
  • 7
  • 17
3

This is what I would do.

Since you have a finite amount of screens, I would make an array to save each instance of the ScreenSlidePageFragment you make under a corresponding index. So you can get an instance from that array by using "position". This will allow you to leverage getItem function to do something like

...
private ScreenSlidePageFragment[] pages = new ScreenSlidePageFragment[NUM_PAGES];
...

@Override
public Fragment getItem(int position) {
    boolean moveRight = mPager.getCurrentItem() < position;
    switch(position){
        case 1: 
            if(moveRight)
                return getPageByPosition(3);
            return getPageByPosition(2);
        case 2: 
            if(moveRight)
                //case when you are in a position 3 and swipe right - return first screen
                return getPageByPosition(3);
            //if you're swiping left from position 1, return the next screen
            return getPageByPosition(1);
        case 3:
        //write out the rest of your logic 
        ...
    }
}

private Fragment getPageByPosition(int position){
    int index = position - 1;
    if(index < 0 || index > NUM_PAGES-1)
        throw new InvalidParameterException("requested position is invalid")

    if(pages[index] == null)
        pages[index] = new ScreenSlidePageFragment();
    return pages[index]; 
}

Note: code not tested

Basically, you will need to write out the logic to return an existing instance of a fragment based on where you are currently and which position is requested.

C0D3LIC1OU5
  • 8,600
  • 2
  • 37
  • 47
  • I've implemented your code, but it did not work. I simplified the process, so I get less problems and tested, whether I can get to the first page if I swipe to the right on the 5th one - no success. I've updated the code – Oleksandr Firsov Oct 02 '15 at 14:45
  • 1
    did you debug? which fragment gets returned when `position` is `5`? – C0D3LIC1OU5 Oct 02 '15 at 14:58
  • I also wonder how you can tell which one is which since you aren't passing anything into the fragment instance. If there's some UI that can be made to look differently you might also want to check your code to make sure you save/restore fragments states properly since fragments should execute `onPause` and `onResume` when they are in and out of the view – C0D3LIC1OU5 Oct 02 '15 at 15:09
  • I added Log.w that gives me a message each time I swipe my finger to the right on specified page(look at updated code). It gives me a `Log`, when I use case 4, but it don't work with case 5. Although Log shows up, it does not transfer me to the first page, though. I do not need to see content on the fragments as of now - I look only at transition itself. – Oleksandr Firsov Oct 02 '15 at 15:17
  • take a look here, i think it'll be helpful http://stackoverflow.com/questions/12141346/circular-viewpager-which-uses-fragmentpageradapter – C0D3LIC1OU5 Oct 02 '15 at 15:34
  • I've checked your links. There sure has to be an easier way. – Oleksandr Firsov Oct 02 '15 at 20:49
  • I think the problem is that when `position` is `1` and you swipe right, adapter won't call your `getItem` method, because it knows that `position = 0 ` is not a valid position. I think this is your biggest problem. – C0D3LIC1OU5 Oct 05 '15 at 15:24
2

You can try ViewPager.setCurrentItem

If you want to navigate to page

viewPager.setCurrentItem(PAGE_POS);

If you want to navigate to page without smooth scroll

viewPager.setCurrentItem(PAGE_POS,false);
Kishan Vaghela
  • 7,678
  • 5
  • 42
  • 67
1

I think it's possible to do. 1. Set ViewPager's item count to Integer.MAX_VALUE and current item to Integer.MAX_VALUE/2. It gives you ability to create fake infinite scroll. 2. Realize your logic depending on fragment on current position. It is easy if you store them in your adapter:

    @Override
    public Fragment getItem(int position) {
        ItemFragmentRoot fragment = new ItemFragmentRoot();
        mFragmentMap.put(position, fragment);
        return fragment;
    }

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

    public ItemFragmentRoot getItem(int position){
        return mFragmentMap.get(position);
    }
  1. And the most difficult part, I think. Since ViewPager cache at least 1 view each side and you don't know which way user going to scroll the only one way is to manualy initialize and reinitialize appearing fragment according to scroll side. Use nested fragment inside each ItemFragmentRoot I mentioned before. All depends on how heavy your view hierarchy.

I can write more code later, if you need.

Dmytro
  • 823
  • 11
  • 14