0

How can I create a ViewPager that contains pages that are created dynamically? There is no limitation on the number of pages. Right from the start the user could navigate either (swipe) left or right to get the previous or the next page. So there is no 'page 0' (in a list).

An application could be a book. It could be a weather (or data) app showing data from previous days or a weather forecast for days ahead. In the book case, after restarting the app could be on page 35. When the user navigates to the previous page, immediately that page 34 could be shown. SO - working with a list of Fragments does not work.

UPDATE: I removed the source code from the question because I have created a complete solution. It's in a give Github project.

tm1701
  • 7,307
  • 17
  • 79
  • 168

1 Answers1

0

The solution to make the ViewPager circular is based on the suggestions of portfoliobuider's post and tobi_b's post.

You can find a nice example in this Github project. Each page (or fragment) is fully dynamically generated. Navigating back or forward is supported (so circular). Of course it has AndroidX as a base.

The Activity is:

public class MainActivity extends FragmentActivity {
    static MainActivity mainActivity;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mainActivity = this;
        if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(android.R.id.content,
                            new PagerFragment()).commit();
        }
    }
}

The PagerFragment is:

public class PagerFragment extends Fragment {
    private static final int SET_ITEM_DELAY = 300;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View result=inflater.inflate(R.layout.pager, container, false);
        final ViewPager pager= result.findViewById(R.id.pager);
        pager.addOnPageChangeListener( new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }
            @Override
            public void onPageSelected( final int position) {
                pager.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        handleSetCurrentItem(position);
                    }
                }, SET_ITEM_DELAY);
            }
            @Override
            public void onPageScrollStateChanged(int state) {
            }
            private void handleSetCurrentItem(final int position) {
                final int lastPosition = pager.getAdapter().getCount() - 1;
                if (position == 0) {
                    pager.setCurrentItem(lastPosition - 1, false);
                } else if (position == lastPosition) {
                    pager.setCurrentItem(1, false);
                }
            }
        });
        pager.setAdapter(buildAdapter());
        pager.setCurrentItem(1); // not 0 !!!
        return(result);
    }
    private PagerAdapter buildAdapter() {
        return(new SampleAdapter(getActivity(), getChildFragmentManager()));
    }
}

The SamplerAdapter is:

public class SampleAdapter extends FragmentStatePagerAdapter {
    private int numberOfPages = 10;
    Context ctxt = null;
    public SampleAdapter(Context ctxt, FragmentManager mgr) {
        super(mgr);
        this.ctxt = ctxt;
    }
    @Override
    public int getCount() {
        return (numberOfPages + 2);
    }

    @Override
    public Fragment getItem(final int position) {
        if (position == 0) {
            return (EditorFragment.newInstance(numberOfPages - 1));
        } else if (position == numberOfPages + 1) {
            return (EditorFragment.newInstance(0));
        } else {
            return (EditorFragment.newInstance(position - 1));
        }
    }
    @Override
    public String getPageTitle(int position) {
        return (EditorFragment.getTitle(ctxt, position));
    }
}

The layout file is:

<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager.widget.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<!--    <androidx.viewpager.widget.PagerTitleStrip could be placed here -->
</androidx.viewpager.widget.ViewPager>

The dynamic Fragment is:

public class EditorFragment extends Fragment {
    private static final String KEY_POSITION = "position";
    static EditorFragment newInstance(int position) {
        EditorFragment frag = new EditorFragment();
        Bundle args = new Bundle();
        args.putInt(KEY_POSITION, position);
        frag.setArguments(args);
        return (frag);
    }
    static String getTitle(Context ctxt, int position) {
        return (String.format(ctxt.getString(R.string.hint), position + 1));
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View result = inflater.inflate(R.layout.editor, container, false);
        EditText editor = (EditText) result.findViewById(R.id.editor);
        int position = getArguments().getInt(KEY_POSITION, -1);
        editor.setHint(getTitle(getActivity(), position));
        return (result);
    }
}

It has a very simple layout file:

<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/editor"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:inputType="textMultiLine"
    android:gravity="left|top" />

This works very nicely!

tm1701
  • 7,307
  • 17
  • 79
  • 168