10

I am using a ViewPager to implement swiping in my android app. However, I would like the previous fragment to be shown when the user uses the back button instead of ending the activity. Is there any way to do that? Thanks Sebastian

Sebastian
  • 103
  • 1
  • 1
  • 7

6 Answers6

8

I had a similar problem, this is how I solved it. I had a ViewPager with 6 fragments and wanted to keep track of the page history and to be able to use the back button to navigate backwards in the history. I create a java.util.Stack<Integer> object, add fragment numbers to it (except when you use the back button, see below), and override onBackPressed() to make it pop the last viewed fragment instead of using the back stack, when my history stack is not empty.

You want to avoid pushing elements on the Stack when you press the back button, otherwise you will get stuck between two fragments if you keep using the back button, instead of eventually exiting.

My code:

MyAdapter mAdapter;
ViewPager mPager;
Stack<Integer> pageHistory;
int currentPage;
boolean saveToHistory;

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

    mAdapter = new MyAdapter(getSupportFragmentManager());
    mPager = (ViewPager)findViewById(R.id.container);
    mPager.setAdapter(mAdapter);
    mPager.setOffscreenPageLimit(5);

    pageHistory = new Stack<Integer>();
    mPager.setOnPageChangeListener(new OnPageChangeListener() {

        @Override
        public void onPageSelected(int arg0) {
            if(saveToHistory)
                pageHistory.push(Integer.valueOf(currentPage));
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }
    });
    saveToHistory = true;
}

@Override
public void onBackPressed() {
    if(pageHistory.empty())
        super.onBackPressed();
    else {
        saveToHistory = false;
        mPager.setCurrentItem(pageHistory.pop().intValue());
        saveToHistory = true;
    }
};
Piero Nicolli
  • 196
  • 1
  • 5
  • The value of `currentPage` in your code is never set. I solved this by instead using a variable called `lastPage` and setting it to the argument of `onPageSelected` after the pushing to stack. `arg0` is the page being navigated to, so we need to use that variable to keep track of the "previous page." `lastPage` should be updated even when `saveToHistory` is false. – Trex Jun 23 '21 at 01:33
7

Overriding below method in Fragment Activity should solve your issue.

@Override
public void onBackPressed() {
    if (mViewPager.getCurrentItem() == 0) {
        // If the user is currently looking at the first step, allow the system to handle the
        // Back button. This calls finish() on this activity and pops the back stack.
        super.onBackPressed();
    } else {
        // Otherwise, select the previous step.
        mViewPager.setCurrentItem(mViewPager.getCurrentItem() - 1);
    }
}
refactor
  • 13,954
  • 24
  • 68
  • 103
5

Override the functionality of your Activity:

public class Activity extends Activity
{
    @Override
    public void onBackPressed()
    {
        // do stuff
        myFragment.onBackPressed();
    }
}

public class Fragment extends Fragment
{

    public void onBackPressed()
    {
        // do stuff
    }
}
DroidBender
  • 7,762
  • 4
  • 28
  • 37
2

If you are using the Jetpack Navigation component, chances are you have a single activity and are trying to manage the backstack inside a fragments. In which case the OnBackPressedDispatcher comes handy.

ancyrweb
  • 1,220
  • 12
  • 12
1

I have same issue and i follow this step (Edited my answer--- Certainly work for me, give it a try)

In the main activity where there are 3 fragment in viewpager i create stack and push and pop data.

private Stack<Integer> stackkk;
private ViewPager mPager;
private int tabPosition = 0;



    mTabLayout.setupWithViewPager(mPager);
    mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
    mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            tabPosition = tab.getPosition();
            mPager.setCurrentItem(tab.getPosition());

// here i have add the data into the stack in each tab click, as at first it will always be null so i add 0 position

            if (stackkk.empty())
                stackkk.push(0);

            if (stackkk.contains(tabPosition)) {
                stackkk.remove(stackkk.indexOf(tabPosition));
                stackkk.push(tabPosition);
            } else {
                stackkk.push(tabPosition);
            }
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {
            tabPositionUnselected = tab.getPosition();
        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {
        }
    });
}

and in the onBackPressed in activity,

//OnbackPress i have first taken out the last one as it already and
//selected by poping it out then only set to the pager.
@Override
public void onBackPressed() {
    if (stackkk.size() > 1) {
        stackkk.pop();
        mPager.setCurrentItem(stackkk.lastElement());
    } else {
    }
}

Hope this may help or msg me.

Ravikant Paudel
  • 2,228
  • 2
  • 17
  • 28
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – Jaiprakash Soni Oct 09 '15 at 04:15
  • @JaiprakashSoni thanks for suggestion. i have edited my answer. Hope this may help others – Ravikant Paudel Oct 09 '15 at 04:25
0

With reference of @Piero Nicolli code with kotlin

This anwser will work same as instagram navigation once you have visited same tab you are not going to visit it again

  val pageHistory = Stack<Int>()
  var saveToHistory = false
  var viewPager: ViewPager? = null

inside Oncreate or OncreateView if you are using viewPager in Fragment

viewPager?.adapter = viewPagerAdapter

 pageHistory.push(0);
        viewPager?.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrolled(
                position: Int,
                positionOffset: Float,
                positionOffsetPixels: Int,
            ) {

            }

            override fun onPageSelected(position: Int) {
                if (saveToHistory) {
                    if (pageHistory.contains(position)) {
                        pageHistory.remove(position)
                        pageHistory.push(position);
                    } else {
                        pageHistory.push(position);
                    }
                }
            }

            override fun onPageScrollStateChanged(state: Int) {
            }
        })
        saveToHistory = true;
        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
            // Handle the back button event
            Log.i(TAG, "pageHistory size ${pageHistory.size}")
            if (pageHistory.size > 1) {

                saveToHistory = false;
                pageHistory.pop()
                viewPager?.currentItem = pageHistory.peek()
                saveToHistory = true;

            } else {
                Log.i(TAG, "pageHistory inside 0 size ${pageHistory.size}")

                if(pageHistory.size ==1){
                    pageHistory.pop()
                }
                if (viewPager?.currentItem == 0){
                    requireActivity().finish()
                }else{
                    viewPager?.currentItem = 0
                }
            }

        }

As I am using Fragment there is callback onBackPressedDispatcher for back button. If you are using viewpager in Activity then copy code from onBackPressedDispatcher() into onBackPressed()

Pratibha
  • 31
  • 5