0

I have a tablayout with a viewpager2 and FragmentStateAdapter. I have 3 tabs, each with a NestedScrollView that wraps a Linear Layout:

<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:id="@+id/scrollView"
        android:focusableInTouchMode="true"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:descendantFocusability="blocksDescendants">

            /* ... */

       </LinearLayout>
</androidx.core.widget.NestedScrollView>

When I switch between the tabs in the tablayout, the scrollview does not start at the top. In the onViewCreated() method of each Fragment for the viewPager, I added the following lines, however the scrollview still does not scroll to the top, it starts where it was left off.

public static class MyFragment extends Fragment {

        private NestedScrollView scrollView;

        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.fragment, container, false);
        }

        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            scrollView = view.findViewById(R.id.scrollView);
            scrollView.smoothScrollTo(0, 0);
        }
}

enter image description here

2 Answers 2

2

The solution from @star4z should work, but there is an easier option. What you could do instead, use FragmentPagerAdapter for this and set it's behavior to BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT.

Every time, you change your tab, onResume will be called and there you can call your scrollView.smoothScrollTo(0, 0);

public class MyAdapter extends FragmentPagerAdapter {
...
        public MyAdapter(FragmentManager fm) {
            super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        }
...
    
}

And then in your Fragment:

public class MyFragment extends Fragment {

...

    public void onResume() {
        super.onResume();
        scrollView.smoothScrollTo(0, 0);
    }

...

}
Sign up to request clarification or add additional context in comments.

1 Comment

the only problem is that I am using viewPager2 with FragmentStateAdapter. How would you do it with for that?
1
+50

onViewCreated is only called when the Fragments are first created. A ViewPager does not change the state of the Fragments. You can think of the hidden Fragments being stored, visible, to the sides of the currently visible Fragment.

I accomplished by setting up a callback when the TabLayout changes. The TabLayout is where the listener for this is. You can then have it call a callback method in your FragmentPagerAdapter. In each Fragment, you should implement a method that returns a callback. In the FragmentPagerAdapter, use the getItem(int position) to store the callback from the Fragment in a Map. The callback method in the adapter takes the position from the TabLayout

First, in your Fragments, give them a method that returns some kind of callback (I used a Runnable) that will be called from the adapter.

public class PlaceHolderFragment extends Fragment {

...

    public Runnable getOnTabChangedListener() {
        return new Runnable() {
            @Override
            public void run() {
                scrollView.smoothScrollTo(0, 0);
            }
        };
    }

...

}

Then, in your FragmentPagerAdapter (I called mine SectionsPagerAdapter), store the callbacks in a map in the getItem() method, and then add a custom callback.

public class SectionsPagerAdapter extends FragmentPagerAdapter {

    ...

    HashMap<Integer, Runnable> tabChangedCallbacks = new HashMap<>();

    ...

    @Override
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page.
        // Return a PlaceholderFragment (defined as a static inner class below).
        PlaceholderFragment fragment = PlaceholderFragment.newInstance(position + 1);
        tabChangedCallbacks.put(position, fragment.getOnTabChangedListener());
        return fragment;
    }

    public void onTabChanged(int position) {
        tabChangedCallbacks.get(position).run();
    }

    ...

}

In your activity that contains the TabLayout, you will want to call addOnTabSelectedListener() on the TabLayout in the onCreate() method.

protected void onCreate(Bundle savedInstanceState) {

    ...

    final SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
    final ViewPager viewPager = findViewById(R.id.view_pager);
    viewPager.setAdapter(sectionsPagerAdapter);
    TabLayout tabs = findViewById(R.id.tabs);
    tabs.setupWithViewPager(viewPager);
    tabs.addOnTabSelectedListener(
            new TabLayout.ViewPagerOnTabSelectedListener(viewPager) {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                    super.onTabSelected(tab);
                    sectionsPagerAdapter.onTabChanged(tab.getPosition());
                }
            });

    ...

}

EDIT

The principle is the same, but for this to work using ViewPager2 and FragmentStateAdapter, you will need the follow changes:

The Fragment stays the same.

The adapter is the same, functionally, with some updated methods:

public class SectionsStateAdapter extends FragmentStateAdapter {

    HashMap<Integer, Runnable> tabChangedCallbacks = new HashMap<>();

    ...

    public void onTabChanged(int position) {
        tabChangedCallbacks.get(position).run();
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        PlaceholderFragment fragment = PlaceholderFragment.newInstance(position + 1);
        tabChangedCallbacks.put(position, fragment.getOnTabChangedListener());
        return fragment;
    }

    @Override
    public int getItemCount() {
        return 2;
    }

    ...
}

The activity changes the most:

public class MainActivity extends AppCompatActivity {

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        ...

        final SectionsStateAdapter sectionsStateAdapter = new SectionsStateAdapter(this);
        final ViewPager2 viewPager = findViewById(R.id.view_pager);
        viewPager.setAdapter(sectionsStateAdapter);
        TabLayout tabs = findViewById(R.id.tabs);
        // connect the TabLayout to the ViewPager2
        new TabLayoutMediator(tabs, viewPager, new TabLayoutMediator.TabConfigurationStrategy() {
            @Override
            public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                // set tab text, etc
            }
        }).attach();
        // set the change listener on the ViewPager2
        viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                sectionsStateAdapter.onTabChanged(position);
            }
        });

        ...

    }
}

Note that the only real change is that the ViewPager2 doesn't need the TabLayout to handle the page change. You could still use TabLayout.addOnTabSelectedListener() if you wanted, but you would have to implement your own TabLayout.OnTabSelecterListener since TabLayout.ViewPagerOnTabSelectedListener doesn't work with ViewPager2.

For example:

tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                sectionsStateAdapter.onTabChanged(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

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

            }
        });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.