ViewPager2 fragments overlap tabbed items

viewpager2 & tablayout
viewpager2 get current fragment
android tabbed activity with fragments
viewpager2 tablayout fragments
viewpager2 wrap content
fragmentstatepageradapter in viewpager2
viewpager2 pagination
how to achieve showing multiple pages at the same time via viewpager2

I've the below code for Viewpager2, I've 2 main issues:

  1. The fragment content is covering the tabs headers. How can I adjust it so that the content starts below the lines under the tab names.
  2. The tabs items are not responding to onTabSelectedListener,

activity_app.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".AppActivity">


    <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>


</androidx.coordinatorlayout.widget.CoordinatorLayout>

AppActivity

import android.net.Uri
import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.android.synthetic.main.activity_app.*

class AppActivity : AppCompatActivity(), BlankFragment.OnFragmentInteractionListener {
    override fun onFragmentInteraction(uri: Uri) {
       // TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
    // private val arrayList = arrayListOf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_app)

        viewpager.adapter = AppViewPagerAdapter(supportFragmentManager, lifecycle)

        TabLayoutMediator(tabs, viewpager, object : TabLayoutMediator.OnConfigureTabCallback {
            override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
                // Styling each tab here
                tab.text = "Tab $position"
            }
        }).attach()
    }
}

AppViewPagerAdapter

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter


class AppViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
    FragmentStateAdapter(fragmentManager, lifecycle) {
    override fun getItem(position: Int): Fragment {

        return when (position) {
            0 -> BlankFragment()
            1 -> BlankFragment()
            else -> BlankFragment()
        }
    }

    override fun getItemCount(): Int {
        return 3
    }
}

fragment_blank.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:tools="http://schemas.android.com/tools"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             tools:context=".BlankFragment">

    <!-- TODO: Update blank fragment layout -->


    <Button
            android:text="Button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:id="@+id/button"/>
</FrameLayout>

TabLayoutMediator

package com.google.android.material.tabs

import androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_DRAGGING
import androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE
import androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import java.lang.ref.WeakReference

/**
 * A mediator to link a TabLayout with a ViewPager2. The mediator will synchronize the ViewPager2's
 * position with the selected tab when a tab is selected, and the TabLayout's scroll position when
 * the user drags the ViewPager2.
 *
 *
 * Establish the link by creating an instance of this class, make sure the ViewPager2 has an
 * adapter and then call [.attach] on it. When creating an instance of this class, you must
 * supply an implementation of [OnConfigureTabCallback] in which you set the text of the tab,
 * and/or perform any styling of the tabs that you require.
 */
class TabLayoutMediator(
    private val tabLayout: TabLayout,
    private val viewPager: ViewPager2,
    private val autoRefresh: Boolean,
    private val onConfigureTabCallback: OnConfigureTabCallback
) {
    private var adapter: RecyclerView.Adapter<*>? = null
    private var attached: Boolean = false

    private var onPageChangeCallback: TabLayoutOnPageChangeCallback? = null
    private var onTabSelectedListener: TabLayout.OnTabSelectedListener? = null
    private var pagerAdapterObserver: RecyclerView.AdapterDataObserver? = null

    /**
     * A callback interface that must be implemented to set the text and styling of newly created
     * tabs.
     */
    interface OnConfigureTabCallback {
        /**
         * Called to configure the tab for the page at the specified position. Typically calls [ ][TabLayout.Tab.setText], but any form of styling can be applied.
         *
         * @param tab The Tab which should be configured to represent the title of the item at the given
         * position in the data set.
         * @param position The position of the item within the adapter's data set.
         */
        fun onConfigureTab(tab: TabLayout.Tab, position: Int)
    }

    constructor(
        tabLayout: TabLayout,
        viewPager: ViewPager2,
        onConfigureTabCallback: OnConfigureTabCallback
    ) : this(tabLayout, viewPager, true, onConfigureTabCallback) {
    }

    /**
     * Link the TabLayout and the ViewPager2 together.
     *
     * @throws IllegalStateException If the mediator is already attached, or the ViewPager2 has no
     * adapter.
     */
    fun attach() {
        if (attached) {
            throw IllegalStateException("TabLayoutMediator is already attached")
        }
        adapter = viewPager.adapter
        if (adapter == null) {
            throw IllegalStateException(
                "TabLayoutMediator attached before ViewPager2 has an " + "adapter"
            )
        }
        attached = true

        // Add our custom OnPageChangeCallback to the ViewPager
        onPageChangeCallback = TabLayoutOnPageChangeCallback(tabLayout)
        viewPager.registerOnPageChangeCallback(onPageChangeCallback!!)

        // Now we'll add a tab selected listener to set ViewPager's current item
        onTabSelectedListener = ViewPagerOnTabSelectedListener(viewPager)
        tabLayout.addOnTabSelectedListener(onTabSelectedListener!!)

        // Now we'll populate ourselves from the pager adapter, adding an observer if
        // autoRefresh is enabled
        if (autoRefresh) {
            // Register our observer on the new adapter
            pagerAdapterObserver = PagerAdapterObserver()
            adapter!!.registerAdapterDataObserver(pagerAdapterObserver!!)
        }

        populateTabsFromPagerAdapter()

        // Now update the scroll position to match the ViewPager's current item
        tabLayout.setScrollPosition(viewPager.currentItem, 0f, true)
    }

    /** Unlink the TabLayout and the ViewPager  */
    fun detach() {
        adapter!!.unregisterAdapterDataObserver(pagerAdapterObserver!!)
        tabLayout.removeOnTabSelectedListener(onTabSelectedListener!!)
        viewPager.unregisterOnPageChangeCallback(onPageChangeCallback!!)
        pagerAdapterObserver = null
        onTabSelectedListener = null
        onPageChangeCallback = null
        attached = false
    }

    internal fun populateTabsFromPagerAdapter() {
        tabLayout.removeAllTabs()

        if (adapter != null) {
            val adapterCount = adapter!!.itemCount
            for (i in 0 until adapterCount) {
                val tab = tabLayout.newTab()
                onConfigureTabCallback.onConfigureTab(tab, i)
                tabLayout.addTab(tab, false)
            }

            // Make sure we reflect the currently set ViewPager item
            if (adapterCount > 0) {
                val currItem = viewPager.currentItem
                if (currItem != tabLayout.selectedTabPosition) {
                    tabLayout.getTabAt(currItem)!!.select()
                }
            }
        }
    }

    /**
     * A [ViewPager2.OnPageChangeCallback] class which contains the necessary calls back to the
     * provided [TabLayout] so that the tab position is kept in sync.
     *
     *
     * This class stores the provided TabLayout weakly, meaning that you can use [ ][ViewPager2.registerOnPageChangeCallback] without removing the
     * callback and not cause a leak.
     */
    private class TabLayoutOnPageChangeCallback internal constructor(tabLayout: TabLayout) :
        ViewPager2.OnPageChangeCallback() {
        private val tabLayoutRef: WeakReference<TabLayout> = WeakReference(tabLayout)
        private var previousScrollState: Int = 0
        private var scrollState: Int = 0

        init {
            reset()
        }

        override fun onPageScrollStateChanged(state: Int) {
            previousScrollState = scrollState
            scrollState = state
        }

        override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
            val tabLayout = tabLayoutRef.get()
            if (tabLayout != null) {
                // Only update the text selection if we're not settling, or we are settling after
                // being dragged
                val updateText = scrollState != SCROLL_STATE_SETTLING || previousScrollState == SCROLL_STATE_DRAGGING
                // Update the indicator if we're not settling after being idle. This is caused
                // from a setCurrentItem() call and will be handled by an animation from
                // onPageSelected() instead.
                val updateIndicator =
                    !(scrollState == SCROLL_STATE_SETTLING && previousScrollState == SCROLL_STATE_IDLE)
                tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator)
            }
        }

        override fun onPageSelected(position: Int) {
            val tabLayout = tabLayoutRef.get()
            if (tabLayout != null
                && tabLayout.selectedTabPosition != position
                && position < tabLayout.tabCount
            ) {
                // Select the tab, only updating the indicator if we're not being dragged/settled
                // (since onPageScrolled will handle that).
                val updateIndicator =
                    scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_SETTLING && previousScrollState == SCROLL_STATE_IDLE
                tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator)
            }
        }

        internal fun reset() {
            scrollState = SCROLL_STATE_IDLE
            previousScrollState = scrollState
        }
    }

    /**
     * A [TabLayout.OnTabSelectedListener] class which contains the necessary calls back to the
     * provided [ViewPager2] so that the tab position is kept in sync.
     */
    private class ViewPagerOnTabSelectedListener internal constructor(private val viewPager: ViewPager2) :
        TabLayout.OnTabSelectedListener {

        override fun onTabSelected(tab: TabLayout.Tab) {
            viewPager.setCurrentItem(tab.position, true)
        }

        override fun onTabUnselected(tab: TabLayout.Tab) {
            // No-op
        }

        override fun onTabReselected(tab: TabLayout.Tab) {
            // No-op
        }
    }

    private inner class PagerAdapterObserver internal constructor() : RecyclerView.AdapterDataObserver() {

        override fun onChanged() {
            populateTabsFromPagerAdapter()
        }

        override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
            populateTabsFromPagerAdapter()
        }

        override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
            populateTabsFromPagerAdapter()
        }

        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            populateTabsFromPagerAdapter()
        }

        override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
            populateTabsFromPagerAdapter()
        }

        override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
            populateTabsFromPagerAdapter()
        }
    }
}

Screen capture of the result

Here is the complete code TabLayout-with-viewpager2-

There is no issue with ViewPager2

The issue is with your layout file

check the result when i change your CoordinatorLayout with LinearLayout

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        android:orientation="vertical"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>


</LinearLayout>

OUTPUT

Follow these steps

Update your dependencies

implementation 'com.google.android.material:material:1.1.0-alpha08'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'

No need to create a custom class for TabLayoutMediator it will include in implementation 'com.google.android.material:material:1.1.0-alpha08'

SAMPLE CODE

R.layout.activity_main

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|enterAlways"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

        <com.google.android.material.tabs.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager"
            app:layout_anchor="@id/tabs"
            app:layout_anchorGravity="bottom"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
    />


</androidx.coordinatorlayout.widget.CoordinatorLayout>

MainActivity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import com.google.android.material.tabs.TabLayoutMediator

import com.google.android.material.tabs.TabLayout


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

//        setSupportActionBar(toolbar)
        viewpager.adapter = AppViewPagerAdapter(supportFragmentManager, lifecycle)

        TabLayoutMediator(tabs, viewpager, object : TabLayoutMediator.OnConfigureTabCallback {
            override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
                // Styling each tab here
                tab.text = "Tab $position"
            }
        }).attach()


    }
}

OUTPUT

Migrate from ViewPager to ViewPager2, Vertical orientation support; Right-to-left support; Modifiable fragment collections fragments using ViewPager2 and Create swipe views with tabs using First, replace the ViewPager elements in your XML layout files with  If you generated ViewPager activity with fragment using Android Studio -> File -> New -> Activity -> Tabbed Acvitity, you shall notice bottom part of the fragment content is out of screen. <android.support.design.widget.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Fragment 2" /> </android.

Try this, add if else statement in TabLayoutMediator.onConfigureTab, it work for me. I not code for Kotlin.

TabLayoutMediator(tabs, viewpager, object : TabLayoutMediator.OnConfigureTabCallback {
        override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
            // if (position == 0){tab.setText("First Tab");}
            tab.text = "Tab $position"
        }
    }).attach()

Separate Back Navigation for a Tabbed View Pager in Android, Navigation is realized by a host fragment for every tab that can be able to press on UI elements to move to other views inside the current tab  We can use the ViewPager to display a tabbed indicator in order to create tabs to display our fragments. At Google I/O 2015, Google announced a new TabLayout class that makes creating this tabbed interface fairly easy to do. See Google Play Style Tabs using TabLayout for a walkthrough.

In my case, viewpager and viewpager2 with tablayout was working but data is not refreshing.

I solved this problem in Viewpager2 by adding adapter.notifyItemChanged(position); as below:

viewPager = (ViewPager2) android.findViewById(R.id.view_pager);
    tabLayout = (TabLayout) android.findViewById(R.id.tab_layout);


    ViewPagerAdapter adapter = new ViewPagerAdapter(getFragmentManager(),getLifecycle());
    viewPager.setAdapter(adapter);

    viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels);
        }

        @Override
        public void onPageSelected(int position){
            super.onPageSelected(position);
            adapter.notifyItemChanged(position);
        }
    });

The above code will refresh the code each time you click the tab. but not when sliding the viewpager.

Android Tab Layout with Swipeable Views, Android tutorial about implementing tab layout using fragments and public int getCount() { // get item count - equal to number of tabs return 3; } } Android working with ViewPager2, TabLayout and Page Transformers Drawer should overlap the titles of the Tabs which I am unable to do as they are set in  Replacing fragments in a viewpager is quite involved but is very possible and can look super slick. First, you need to let the viewpager itself handle the removing and adding of the fragments. What is happening is when you replace the fragment inside of SearchFragment, your viewpager retains its fragment views.

Android Material Design working with Tabs, Android tutorial about implementing material design tabs in your app. Explained <item name="android:windowAllowEnterTransitionOverlap">true</item> <item package info.androidhive.materialtabs.fragments; import android.os. Android working with ViewPager2, TabLayout and Page Transformers. Below is my code which has 3 Fragment classes each embedded with each of the 3 tabs on ViewPager.I have a menu option. As shown in the onOptionsItemSelected(), by selecting an option, I need to update the fragment that is currently visible.

Create a Parallax Scrolling Header with Tabs in Android, I have 3 pages in my ViewPager2. DrawerLayout in activity with custom onClicks + ViewPager2 in fragment? ViewPager2 fragments overlap tabbed items. The blue tabs are at the Main Activity level and the gray date tabs are in the selected fragment view. I have read a few post about using Tab Host or Fragment Activity, or do I use Activity that is extended to Fragment?

Dynamic ViewPager Indicators With RecyclerView Items in Android, In this post, we'll look at making parallax scrolling Tabs using Android Design Support Toolbar and Tabs overlap on collapse So take note of the ordering of elements inside. A minimal Adapter for our ViewPager (to hold 3 Fragments). I want to add dynamic tab items. I have a fragment which is FragmentOne and it has a TextView. I'm trying create FragmentOne in foreach and add to tabs. I tested code which is in setupViewPager but it doesn't work. How can I edit TextView which in fragments? if I remove this lines it works but contents of fragment always show default that

Comments
  • Have you attempted to use Kotlin?
  • Thanks, how can I add layout to the tab instead of just using tab.text = "Tab $position"
  • @HasanAYousef didn't get you can you explore more
  • I want to display fragment in the tab, not just text element
  • @HasanAYousef you want to display fragment name instead of tab.text = "Tab $position"
  • I got this error: ` java.lang.NoSuchMethodError: No static method metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; in class Ljava/lang/invoke/LambdaMetafactory; or its super classes (declaration of 'java.lang.invoke.LambdaMetafactory' appears in /apex/com.android.runtime/javalib/core-oj.jar) at com.google.android.material.tabs.TabLayout$TabView.addOnLayoutChangeListener(TabLayout.java:2592)`