6

I have an Activity with ViewPager. the ViewPager have a lot of pages, Just like a book. Each fragment has RecyclerViews with a lot of content. The following is my use case

1 - When I swipe page, RecyclerView must start from the beginning of the list. 2. If I rotate my device, It should read from exact last position of where I left before rotation.

If I don't use any logic, 2nd scenario works perfectly. ie, rotation. But not the first scenario.

If I do some logic like below. Helps to achieve first scenario, But not the second scenario.

  @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
    if (savedInstanceState==null) {
        mIsFirstTime = true;
    }
}

@Override
public void onResume() {
    super.onResume();
    try {
        getActivity().invalidateOptionsMenu();
            if (!mIsFirstTime) {
                mListView.scrollToPosition(0);                
            }
    } catch (Exception e) {
        e.printStackTrace();
    }

}

So My question is

How can I determine a fragment restart is due to screen rotation or Viewpager swipe?

I already tried onConfigurationChanged(Configuration newConfig) . But it doesn't help me. Could you please help me

azizbekian
  • 60,783
  • 13
  • 169
  • 249
Vinayak B
  • 4,430
  • 4
  • 28
  • 58
  • Let me correct, You have one activity with viewPager, And each page having its own fragment, a fragment having recyclerview? – Jitesh Mohite Apr 25 '18 at 05:48
  • @jiteshmohite I have one activity with a view pager. this view pager have only one fragment with a recyclerview . the data will change according to a list. – Vinayak B Apr 25 '18 at 06:57

4 Answers4

2

From a comment of OP:

My question is how I know that onSaveInstanceState() is called due to screen orientation change and not fragment restart

You can do that via Activity#isChangingConfigurations() API.


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        if (isChangingConfigurations()) {
            // this callback is executed as a result of a configuration change (e.g. orientation change)
        } else {
            // no configuration change happens (e.g. a click on a home button would end up here)
        }
    }

Now you can save a boolean value into Bundle outState and appropriate setup UI after recreation:


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("isConfigurationChange", isChangingConfigurations());
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // initialization logics here

        if (savedInstanceState != null) {
            boolean isConfigurationChange = savedInstanceState.getBoolean("isConfigurationChange");

            // if `onCreate` is called as a result of configuration change
            if (isConfigurationChange) {
                // view hierarchy is not yet laid out, have to post an action 
                listView.post(new Runnable() {
                    @Override
                    public void run() {
                        // we are guaranteed, that `listView` is already laid out
                        listView.scrollToPosition(0);
                    }
                });
            }
        }
    }

Note, that performing boolean value checking logics inside onResume() is a bug (pressing home button and navigating back to the app would result in scrolling to the top again, which is not desired). Instead onCreate() should be chosen as a correct callback.

azizbekian
  • 60,783
  • 13
  • 169
  • 249
  • Let me try this logic and let you know – Vinayak B Apr 26 '18 at 10:24
  • That's for notifying that bug. Your answer is more relevance for my problem. but onSaveInstanceState is not calling when screen rotate. I am overriding onConfigurationChanged . anyway your logic is working. Let me check if there is any other bugs . – Vinayak B Apr 27 '18 at 04:34
  • `but onSaveInstanceState is not calling when screen rotate` It should be called, check whether you have overriden flags in manifest. – azizbekian Apr 27 '18 at 05:11
  • android:configChanges="orientation|keyboardHidden|screenSize" this is the only thing I added in the manifest – Vinayak B Apr 27 '18 at 05:23
  • 1
    That's your issue. Remove that flags and correctly handle orientation changes. – azizbekian Apr 27 '18 at 05:58
1

I think you can declare a variable and check the status to true on swipe action.

viewPager.addOnPageChangeListener(new OnPageChangeListener() {
public void onPageScrollStateChanged(int state) {}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}

public void onPageSelected(int position) {
// here assign the variable to true;
// This method will get called only after a swipe action is invoked on 
//viewpager.  
 }});

Inside your fragment check whether the variable status is true, if so it's not getting generated from orientation, then reset the variable to false.

Happy coding :)

Manoj Perumarath
  • 9,337
  • 8
  • 56
  • 77
0

Inside your Fragment and View Pager make use of onSaveInstanceState() function and use That saved Instance to scroll to an old position.

In cases where the UI data to preserve is simple and lightweight, you might use onSaveInstanceState() alone to preserve your state data. In cases where you have complex data that you want to preserve, you can use a combination of ViewModel objects, the onSaveInstanceState() method, and persistent local storage.

REFER Saving UI States

EXAMPLE FOR USING onSaveInstanceState()

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
    if (savedInstanceState==null) {
            if(savedInstanceState.containsKey("position")){
                int positon = savedInstanceState.getInt("position",0);
                YOUR_RECYCLER_VIEW.scrollToPosition(positon);
        }
    }
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("position",YOUR_RECYLER_VIEW.getVerticalScrollbarPosition());
}

Learn More Here

  1. How to use onSaveInstanceState() and onRestoreInstanceState()?

  2. Android Developers Official Documention

To know the pageChange of viewpage use

myViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        //Use your logic here.

        }

        @Override
        public void onPageSelected(int position) {

        //Use your logic here.

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        //Use your logic here.

        }
    });
Tomin B Azhakathu
  • 2,656
  • 1
  • 19
  • 28
  • onSaveInstanceState also call when screen orientation change, .My question is how I know that onSaveInstanceState is called due to screen orientation change and not fragment restart – Vinayak B Apr 25 '18 at 11:50
  • Try using a flag for saving orientation and save it in the savedStateInstance Bundle – Tomin B Azhakathu Apr 25 '18 at 11:55
  • I tried that. But in my scenario i am using a viewpager. It will work for the first page. but when you scroll to next page it won't work. – Vinayak B Apr 25 '18 at 11:59
  • I am looking for a particular answer,ie, How can I know the cause of a fragment restart. Is this because Orientation change or Viewpager swipe – Vinayak B Apr 25 '18 at 12:00
  • @VinayakB Viewpager swipe can be obtained by adding using `addOnPageChangeListener()` function. Refer my updated answer – Tomin B Azhakathu Apr 25 '18 at 12:04
  • Use interface to acknowldge the Fragment inside the Listener – Tomin B Azhakathu Apr 25 '18 at 12:06
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/169757/discussion-between-vinayak-b-and-tomin-b). – Vinayak B Apr 25 '18 at 12:06
0

Why are you scrolling to zero position? mListView.scrollToPosition(0);

I think you can in onPause of fragment save current position of RecyclerView and then, after recreation - go to that position.

yozhik
  • 4,644
  • 14
  • 65
  • 98
  • 1
    Consider this App is a book. So for every swipe to next page the user needs to read from the first line .that's why I am using scrollToPosition(0) – Vinayak B Apr 27 '18 at 04:34