Android Support Design TabLayout: Center of gravity and scroll mode

I am trying to use a new TabLayout project in my project. I want the layout to adapt to every screen size and orientation, but it can be seen correctly in the same orientation.

I deal with Gravity and Mode, setting my tabLayout as:

tabLayout.setTabGravity(TabLayout.GRAVITY_CENTER); tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); 

Therefore, I expect that if there is no space, the tabLayout scrolls, but if there is space, it is centered.

From the guides:

public static final int GRAVITY_CENTER The gravity used to lay out the tabs in the center of the TabLayout.

public static final int GRAVITY_FILL The gravity used to populate the TabLayout as much as possible. This option is only valid when used with MODE_FIXED.

public static final int MODE_FIXED Fixed tabs display all tabs at once and are best used with content that rotates between tabs. The maximum number of tabs is limited by the width of the views. Fixed tabs have equal widths based on the widest tab tab.

public static final int MODE_SCROLLABLE Scrolling tabs display a subset of tabs at any given time and may contain longer shortcut labels and more tabs. They are best suited for viewing contexts in touch interfaces when users do not need to directly compare the label tab.

So GRAVITY_FILL is only compatible with MODE_FIXED, but nothing is specified in at for GRAVITY_CENTER, I expect it to be compatible with MODE_SCROLLABLE, but this is what I get from using GRAVITY_CENTER and MODE_SCROLLABLE

enter image description here

Thus, it uses SCROLLABLE in both orientations, but does not use GRAVITY_CENTER.

This is what I would expect from a landscape; but for this I need to set MODE_FIXED, so what I get in the portrait:

enter image description here

Why doesn't GRAVITY_CENTER work for SCROLLABLE if tabLayout fits the screen? Is there a way to set gravity and mode dynamically (and see what I expect)?

Thank you very much!

EDITED: this is the layout of my TabLayout tab:

 <android.support.design.widget.TabLayout android:id="@+id/sliding_tabs" android:layout_width="match_parent" android:background="@color/orange_pager" android:layout_height="wrap_content" /> 
+86
android android-5.0-lollipop material-design gravity
Jun 03 '15 at 9:45
source share
13 answers

Since I did not find why this is happening, I used the following code:

 float myTabLayoutSize = 360; if (DeviceInfo.getWidthDP(this) >= myTabLayoutSize ){ tabLayout.setTabMode(TabLayout.MODE_FIXED); } else { tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); } 

Basically, I have to manually calculate the width of my tabLayout, and then I set the Tab mode depending on whether tabLayout is suitable on the device or not.

The reason I get the layout size manually is that not all tabs have the same width in scroll mode, and this may provoke that some names use 2 lines, as happened to me in this example.

+7
Jul 11 '15 at 23:59 on
source share

Effects only for gravity tablets MODE_FIXED .

One possible solution is to set layout_width to wrap_content and layout_gravity to center_horizontal :

 <android.support.design.widget.TabLayout android:id="@+id/sliding_tabs" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" app:tabMode="scrollable" /> 

If the tabs are smaller than the screen width, the TabLayout itself TabLayout also be smaller, and it will be centered due to gravity. If the tabs are larger than the screen width, TabLayout will correspond to the screen width, and scrolling is activated.

+111
Jul 12 '15 at 0:30
source share

this is how i did it

TabLayout.xml

 <android.support.design.widget.TabLayout android:id="@+id/tab_layout" android:layout_height="wrap_content" android:layout_width="wrap_content" android:background="@android:color/transparent" app:tabGravity="fill" app:tabMode="scrollable" app:tabTextAppearance="@style/TextAppearance.Design.Tab" app:tabSelectedTextColor="@color/myPrimaryColor" app:tabIndicatorColor="@color/myPrimaryColor" android:overScrollMode="never" /> 

Oncreate

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mToolbar = (Toolbar) findViewById(R.id.toolbar_actionbar); mTabLayout = (TabLayout)findViewById(R.id.tab_layout); mTabLayout.setOnTabSelectedListener(this); setSupportActionBar(mToolbar); mTabLayout.addTab(mTabLayout.newTab().setText("Dashboard")); mTabLayout.addTab(mTabLayout.newTab().setText("Signature")); mTabLayout.addTab(mTabLayout.newTab().setText("Booking/Sampling")); mTabLayout.addTab(mTabLayout.newTab().setText("Calendar")); mTabLayout.addTab(mTabLayout.newTab().setText("Customer Detail")); mTabLayout.post(mTabLayout_config); } Runnable mTabLayout_config = new Runnable() { @Override public void run() { if(mTabLayout.getWidth() < MainActivity.this.getResources().getDisplayMetrics().widthPixels) { mTabLayout.setTabMode(TabLayout.MODE_FIXED); ViewGroup.LayoutParams mParams = mTabLayout.getLayoutParams(); mParams.width = ViewGroup.LayoutParams.MATCH_PARENT; mTabLayout.setLayoutParams(mParams); } else { mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); } } }; 

I made small changes to @Mario Velasco's solution on the running part

+13
Nov 16 '15 at 9:22
source share

Take a look at android-tablayouthelper

Automatically switch TabLayout.MODE_FIXED and TabLayout.MODE_SCROLLABLE depending on the total width of the tab.

+7
Aug 6 '15 at 16:42
source share

for simplicity just add the application: tabMode = "scrollable" and android: layout_gravity = "bottom"

exactly

 <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:layout_gravity="bottom" app:tabMode="scrollable" app:tabIndicatorColor="@color/colorAccent" /> 

enter image description here

+6
Mar 14 '16 at 13:18
source share

This is the solution I used to automatically change between SCROLLABLE and FIXED + FILL . This is the complete code for @ Fighter42 solution:

(The code below shows where to make changes if you used the Google tab activity template)

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Create the adapter that will return a fragment for each of the three // primary sections of the activity. mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); // Set up the ViewPager with the sections adapter. mViewPager = (ViewPager) findViewById(R.id.container); mViewPager.setAdapter(mSectionsPagerAdapter); // Set up the tabs final TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(mViewPager); // Mario Velasco code tabLayout.post(new Runnable() { @Override public void run() { int tabLayoutWidth = tabLayout.getWidth(); DisplayMetrics metrics = new DisplayMetrics(); ActivityMain.this.getWindowManager().getDefaultDisplay().getMetrics(metrics); int deviceWidth = metrics.widthPixels; if (tabLayoutWidth < deviceWidth) { tabLayout.setTabMode(TabLayout.MODE_FIXED); tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); } else { tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); } } }); } 

Markup:

  <android.support.design.widget.TabLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> 

If you do not need to fill the width, it is better to use @karaokyo solution.

+4
Sep 29 '15 at 12:36
source share

To do this, I created the AdaptiveTabLayout class. This was the only way to find a solution to the problem, answer the question and avoid the problems / problems that may arise in other answers here.

Notes:

  • Handles tablet / tablet mockups.
  • Handles cases when there is enough room for MODE_SCROLLABLE , but not enough space for MODE_FIXED . If you do not deal with this case, this will happen on some devices that you will see different sizes of text or oven two lines of text on some tabs that look bad.
  • It takes real action and makes no assumptions (for example, the screen is 360dp wide or something else ...). This works with real screen sizes and real tab sizes. This means that it works well with translations, because it does not imply a tab size, tabs get a measure.
  • Deals with different passes at the onLayout stage to avoid extra work.
  • Layout width should be wrap_content in xml. Do not set the mode or gravity to xml.

AdaptiveTabLayout.java

 import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; import android.util.AttributeSet; import android.widget.LinearLayout; public class AdaptiveTabLayout extends TabLayout { private boolean mGravityAndModeSeUpNeeded = true; public AdaptiveTabLayout(@NonNull final Context context) { this(context, null); } public AdaptiveTabLayout(@NonNull final Context context, @Nullable final AttributeSet attrs) { this(context, attrs, 0); } public AdaptiveTabLayout ( @NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr ) { super(context, attrs, defStyleAttr); setTabMode(MODE_SCROLLABLE); } @Override protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) { super.onLayout(changed, l, t, r, b); if (mGravityAndModeSeUpNeeded) { setModeAndGravity(); } } private void setModeAndGravity() { final int tabCount = getTabCount(); final int screenWidth = UtilsDevice.getScreenWidth(); final int minWidthNeedForMixedMode = getMinSpaceNeededForFixedMode(tabCount); if (minWidthNeedForMixedMode == 0) { return; } else if (minWidthNeedForMixedMode < screenWidth) { setTabMode(MODE_FIXED); setTabGravity(UtilsDevice.isBigTablet() ? GRAVITY_CENTER : GRAVITY_FILL) ; } else { setTabMode(TabLayout.MODE_SCROLLABLE); } setLayoutParams(new LinearLayout.LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); mGravityAndModeSeUpNeeded = false; } private int getMinSpaceNeededForFixedMode(final int tabCount) { final LinearLayout linearLayout = (LinearLayout) getChildAt(0); int widestTab = 0; int currentWidth; for (int i = 0; i < tabCount; i++) { currentWidth = linearLayout.getChildAt(i).getWidth(); if (currentWidth == 0) return 0; if (currentWidth > widestTab) { widestTab = currentWidth; } } return widestTab * tabCount; } } 

And this is the DeviceUtils class:

 import android.content.res.Resources; public class UtilsDevice extends Utils { private static final int sWIDTH_FOR_BIG_TABLET_IN_DP = 720; private UtilsDevice() {} public static int pixelToDp(final int pixels) { return (int) (pixels / Resources.getSystem().getDisplayMetrics().density); } public static int getScreenWidth() { return Resources .getSystem() .getDisplayMetrics() .widthPixels; } public static boolean isBigTablet() { return pixelToDp(getScreenWidth()) >= sWIDTH_FOR_BIG_TABLET_IN_DP; } } 

Usage example:

 <?xml version="1.0" encoding="utf-8"?> <FrameLayout 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.com.stackoverflow.example.AdaptiveTabLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?colorPrimary" app:tabIndicatorColor="@color/white" app:tabSelectedTextColor="@color/text_white_primary" app:tabTextColor="@color/text_white_secondary" tools:layout_width="match_parent"/> </FrameLayout> 

Gist

Problems / Ask for Help:

  • You will see the following:

Logcat:

 W/View: requestLayout() improperly called by android.support.design.widget.TabLayout$SlidingTabStrip{3e1ebcd6 V.ED.... ......ID 0,0-466,96} during layout: running second layout pass W/View: requestLayout() improperly called by android.support.design.widget.TabLayout$TabView{3423cb57 VFE...C. ..S...ID 0,0-144,96} during layout: running second layout pass W/View: requestLayout() improperly called by android.support.design.widget.TabLayout$TabView{377c4644 VFE...C. ......ID 144,0-322,96} during layout: running second layout pass W/View: requestLayout() improperly called by android.support.design.widget.TabLayout$TabView{19ead32d VFE...C. ......ID 322,0-466,96} during layout: running second layout pass 

I am not sure how to solve it. Any suggestions?

  • To make a TabLayout child, I do some castings and assumptions (Like a LinearLayout child containing other views ...) This may cause problems with further updates to the technical support library. Best approach / suggestions?
+4
May 03 '16 at 13:20
source share

This is the only code that worked for me:

 public static void adjustTabLayoutBounds(final TabLayout tabLayout, final DisplayMetrics displayMetrics){ final ViewTreeObserver vto = tabLayout.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { tabLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); int totalTabPaddingPlusWidth = 0; for(int i=0; i < tabLayout.getTabCount(); i++){ final LinearLayout tabView = ((LinearLayout)((LinearLayout) tabLayout.getChildAt(0)).getChildAt(i)); totalTabPaddingPlusWidth += (tabView.getMeasuredWidth() + tabView.getPaddingLeft() + tabView.getPaddingRight()); } if (totalTabPaddingPlusWidth <= displayMetrics.widthPixels){ tabLayout.setTabMode(TabLayout.MODE_FIXED); tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); }else{ tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); } tabLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); } }); } 

A DisplayMetrics instance can be obtained using the following command:

 public DisplayMetrics getDisplayMetrics() { final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); final Display display = wm.getDefaultDisplay(); final DisplayMetrics displayMetrics = new DisplayMetrics(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { display.getMetrics(displayMetrics); }else{ display.getRealMetrics(displayMetrics); } return displayMetrics; } 

And your TabLayout XML file should look like this (don't forget to set tabMaxWidth to 0):

 <android.support.design.widget.TabLayout xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/tab_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" app:tabMaxWidth="0dp"/> 
+1
Mar 13 '16 at 20:06
source share

All you need to do is add the following to your TabLayout:

 custom:tabGravity="fill" 

So you will have:

 xmlns:custom="http://schemas.android.com/apk/res-auto" <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" custom:tabGravity="fill" /> 
+1
Apr 22 '16 at 4:25
source share

I solved this using the following

 if(tabLayout_chemistCategory.getTabCount()<4) { tabLayout_chemistCategory.setTabGravity(TabLayout.GRAVITY_FILL); }else { tabLayout_chemistCategory.setTabMode(TabLayout.MODE_SCROLLABLE); } 
+1
Aug 11 '17 at 19:50
source share
  <android.support.design.widget.TabLayout android:id="@+id/tabList" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" app:tabMode="scrollable"/> 
0
Jan 28 '19 at 5:13
source share

A very simple example, and it always works.

 /** * Setup stretch and scrollable TabLayout. * The TabLayout initial parameters in layout must be: * android:layout_width="wrap_content" * app:tabMaxWidth="0dp" * app:tabGravity="fill" * app:tabMode="fixed" * * @param context your Context * @param tabLayout your TabLayout */ public static void setupStretchTabLayout(Context context, TabLayout tabLayout) { tabLayout.post(() -> { ViewGroup.LayoutParams params = tabLayout.getLayoutParams(); if (params.width == ViewGroup.LayoutParams.MATCH_PARENT) { // is already set up for stretch return; } int deviceWidth = context.getResources() .getDisplayMetrics().widthPixels; if (tabLayout.getWidth() < deviceWidth) { tabLayout.setTabMode(TabLayout.MODE_FIXED); params.width = ViewGroup.LayoutParams.MATCH_PARENT; } else { tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); params.width = ViewGroup.LayoutParams.WRAP_CONTENT; } tabLayout.setLayoutParams(params); }); } 
0
Apr 08 '19 at 10:50
source share

My final decision

 class DynamicModeTabLayout : TabLayout { constructor(context: Context?) : super(context) constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) override fun setupWithViewPager(viewPager: ViewPager?) { super.setupWithViewPager(viewPager) val view = getChildAt(0) ?: return view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED) val size = view.measuredWidth if (size > measuredWidth) { tabMode = MODE_SCROLLABLE tabGravity = GRAVITY_CENTER } else { tabMode = MODE_FIXED tabGravity = GRAVITY_FILL } } } 
0
Jul 05 '19 at 17:59
source share



All Articles