'Android Tab layout: Wrap tab indicator width with respect to tab title
Solution 1:[1]
Yes, it's possible to wrap tab indicator as title setting padding to 0
public void wrapTabIndicatorToTitle(TabLayout tabLayout, int externalMargin, int internalMargin) {
View tabStrip = tabLayout.getChildAt(0);
if (tabStrip instanceof ViewGroup) {
ViewGroup tabStripGroup = (ViewGroup) tabStrip;
int childCount = ((ViewGroup) tabStrip).getChildCount();
for (int i = 0; i < childCount; i++) {
View tabView = tabStripGroup.getChildAt(i);
//set minimum width to 0 for instead for small texts, indicator is not wrapped as expected
tabView.setMinimumWidth(0);
// set padding to 0 for wrapping indicator as title
tabView.setPadding(0, tabView.getPaddingTop(), 0, tabView.getPaddingBottom());
// setting custom margin between tabs
if (tabView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) tabView.getLayoutParams();
if (i == 0) {
// left
settingMargin(layoutParams, externalMargin, internalMargin);
} else if (i == childCount - 1) {
// right
settingMargin(layoutParams, internalMargin, externalMargin);
} else {
// internal
settingMargin(layoutParams, internalMargin, internalMargin);
}
}
}
tabLayout.requestLayout();
}
}
private void settingMargin(ViewGroup.MarginLayoutParams layoutParams, int start, int end) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
layoutParams.setMarginStart(start);
layoutParams.setMarginEnd(end);
layoutParams.leftMargin = start;
layoutParams.rightMargin = end;
} else {
layoutParams.leftMargin = start;
layoutParams.rightMargin = end;
}
}
EDIT:
As of com.android.support:design:28.0.0
, you can adjust the indicator as label easily now setting:
app:tabIndicatorFullWidth="false"
EDIT July 2019:
Use the material dependency
com.google.android.material:material:x.x.x
Solution 2:[2]
As of support library 28 you can do the following:
app:tabIndicatorFullWidth="false"
app:tabPaddingStart="25dp"
app:tabPaddingEnd="25dp"
You can set the desired padding that affects the tab indicator.
Also you can now do this:
app:tabIndicator="@drawable/tab_indicator"
this will set a custom drawable as indicator.
example of custom drawable:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorPrimary"/>
<corners android:radius="5dp"/>
</shape>
Solution 3:[3]
If you don't need the strip to be smaller than the text, then this should work:
public static void reduceMarginsInTabs(TabLayout tabLayout, int marginOffset) {
View tabStrip = tabLayout.getChildAt(0);
if (tabStrip instanceof ViewGroup) {
ViewGroup tabStripGroup = (ViewGroup) tabStrip;
for (int i = 0; i < ((ViewGroup) tabStrip).getChildCount(); i++) {
View tabView = tabStripGroup.getChildAt(i);
if (tabView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) tabView.getLayoutParams()).leftMargin = marginOffset;
((ViewGroup.MarginLayoutParams) tabView.getLayoutParams()).rightMargin = marginOffset;
}
}
tabLayout.requestLayout();
}
}
For added points you could check the text size of each title.
Solution 4:[4]
One simple solution is:
tabLayout.setTabIndicatorFullWidth(false);
But it works with latest dependency like
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:design:28.0.0'
Solution 5:[5]
Short answer is "no". Here is explanation.
There is private class SlidingTabStrip
within TabLayout that draws indicator
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
// Thick colored underline below the current selection
if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
}
}
I believe mIndicatorLeft and mIndicatorRight is what you need. These fields are set in the same class:
private void setIndicatorPosition(int left, int right) {
if (left != mIndicatorLeft || right != mIndicatorRight) {
// If the indicator's left/right has changed, invalidate
mIndicatorLeft = left;
mIndicatorRight = right;
ViewCompat.postInvalidateOnAnimation(this);
}
}
where left and right parameters are calculated in next method:
private void updateIndicatorPosition() {
final View selectedTitle = getChildAt(mSelectedPosition);
int left, right;
if (selectedTitle != null && selectedTitle.getWidth() > 0) {
left = selectedTitle.getLeft();
right = selectedTitle.getRight();
if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {
// Draw the selection partway between the tabs
View nextTitle = getChildAt(mSelectedPosition + 1);
left = (int) (mSelectionOffset * nextTitle.getLeft() +
(1.0f - mSelectionOffset) * left);
right = (int) (mSelectionOffset * nextTitle.getRight() +
(1.0f - mSelectionOffset) * right);
}
} else {
left = right = -1;
}
setIndicatorPosition(left, right);
}
And the worst thing is that SlidingTabStrip field in TabLayout is private and final.
private final SlidingTabStrip mTabStrip;
I don't see how it's possible to achieve what you need without creating totally new TabLayout.
Solution 6:[6]
A simple solution is to add this attribute to your tabLayout
app:tabIndicatorFullWidth="false"
Solution 7:[7]
You can do this:
layout/layout_demo.xml
<com.google.android.material.tabs.TabLayout
app:tabIndicatorFullWidth="false"
app:tabIndicator="@drawable/tab_indicator"
.../>
res/tab_indicator.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:gravity="center">
<shape>
<size
android:width="51dp"
android:height="3dp" />
</shape>
</item>
</layer-list>
Solution 8:[8]
You can do it using styles.
In the layout xml:
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
style="@style/AppTabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
in the styles.xml:
<style name="AppTabLayout" parent="Widget.Design.TabLayout">
<item name="android:layout_marginLeft">40dp</item>
<item name="android:layout_marginRight">40dp</item>
</style>
Solution 9:[9]
I'm note sure about the SDK Version needed, but you can do it directly in your XML file, adding the padding attribute using app namespace.
Just make your TabLayout looks like this:
<android.support.design.widget.TabLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
app:tabPaddingEnd="0dp"
app:tabPaddingStart="0dp"
</android.support.design.widget.TabLayout>
You can move the xmlns:app="http://schemas.android.com/apk/res-auto"
for your parent layou too, but its your choice :)
Thanks leon for his answer, that lead me to achieve that by XML.
Solution 10:[10]
I tried 2 hours for various solutions but none of those have a perfect effect.
The problem lies on the tabView's padding - even if I set its padding to all 0,
Different tabs still have different padding and therefore the text inside its TextView
varies in size.
Then I found this library which perfectly solved it.
https://github.com/H07000223/FlycoTabLayout
As @Stas Melnychenko said, we probably can't do it unless we re-write a TabLayout
. And this library does re-write a TabLayout
...
Solution 11:[11]
There's a attribute called tabIndicatorFullWidth inside android.support.design.widget.TabLayout
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabBackground="@color/colorPrimary"
app:tabIndicatorFullWidth="false" // tested okay on 8.1 , 6.0.1
app:tabTextColor="@color/colorAccent"
app:tabTextAppearance="@style/CampusTabText"
android:minHeight="?attr/actionBarSize"
app:tabMaxWidth="0dp"
app:tabGravity="fill"
app:tabMode="fixed" />
Solution 12:[12]
We can easily set the width of the Tab indicator with respect to its Tab Title. Because of the com.android.support:design:28.0.0, we can easily adjust it using below code in xml file:
app:tabIndicatorFullWidth="false"
Solution 13:[13]
For me app:tabIndicatorFullWidth="false"
works perfectly. Here is my full code
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicator="@drawable/my_tab_indicator"
app:tabIndicatorColor="@color/tab_selected_unselected"
app:tabIndicatorGravity="bottom"
app:tabIndicatorHeight="@dimen/_3sdp"
app:tabRippleColor="@color/transparent"
app:tabMode="fixed"
app:tabGravity="fill"
app:tabIndicatorFullWidth="false"
app:tabSelectedTextColor="@color/bottom_navigation_menu_selected"
app:tabTextAppearance="@style/TabItemTextAppearance"
app:tabTextColor="@color/title_1">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="?????????" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="??????????" />
</com.google.android.material.tabs.TabLayout>
Solution 14:[14]
You just need to add app:tabMode="scrollable"
. It automatically resizes
Solution 15:[15]
Simply add spaces or spaces on either side of the text of the tab:
tabLayout.addTab(tabLayout.newTab().setText(" Login "));
tabLayout.addTab(tabLayout.newTab().setText(" Sign Up "));
and don't forget to set the attribute:
app:tabIndicatorFullWidth="false"
in active.
here is the result: enter image description here
This is my code:
+) In .xml
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="#D741C9EC"
app:tabIndicatorHeight="3dp"
app:tabSelectedTextColor="#D741C9EC"
app:tabIndicatorFullWidth="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:ignore="MissingConstraints"></com.google.android.material.tabs.TabLayout>
+) In adapter: LoginAdapter.java
public class LoginAdapter extends FragmentPagerAdapter {
Context context;
int tab;
public LoginAdapter(@NonNull FragmentManager fm, Context context, int tab) {
super(fm);
this.context = context;
this.tab = tab;
}
public LoginAdapter(@NonNull FragmentManager fm, int behavior, Context context, int tab) {
super(fm, behavior);
this.context = context;
this.tab = tab;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position){
case 0:
SignInFragment signInFragment = new SignInFragment();
return signInFragment;
case 1:
SignUpFragment signUpFragment = new SignUpFragment();
return signUpFragment;
default:
return null;
}
}
@Override
public int getCount() {
return tab;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return " Login ";
case 1:
return " Sign Up ";
}
return null;
}
}
Solution 16:[16]
Wrap tab indicator width with respect to tab title and add right padding with tab title and right margin with tab indicator.
public class TabLayoutActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_layout);
TabLayout tabs = (TabLayout) findViewById(R.id.tabs);
tabs.setTabTextColors(Color.parseColor("#727272"), Color.parseColor("#305b0d"));
ViewPager pager = (ViewPager) findViewById(R.id.pager);
TabsPagerAdapter adapter = new TabsPagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
tabs.setupWithViewPager(pager);
wrapTabIndicatorToTitle(tabs,20,20);
}
public void wrapTabIndicatorToTitle(TabLayout tabLayout, int externalMargin, int internalMargin) {
View tabStrip = tabLayout.getChildAt(0);
if (tabStrip instanceof ViewGroup) {
ViewGroup tabStripGroup = (ViewGroup) tabStrip;
int childCount = ((ViewGroup) tabStrip).getChildCount();
for (int i = 0; i < childCount; i++) {
View tabView = tabStripGroup.getChildAt(i);
//set minimum width to 0 for instead for small texts, indicator is not wrapped as expected
tabView.setMinimumWidth(0);
// set padding to 0 for wrapping indicator as title
tabView.setPadding(0, tabView.getPaddingTop(), 40, tabView.getPaddingBottom());
// setting custom margin between tabs
if (tabView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) tabView.getLayoutParams();
if (i == 0) {
// left
setMargin(layoutParams, externalMargin, internalMargin);
} else if (i == childCount - 1) {
// right
setMargin(layoutParams, internalMargin, externalMargin);
} else {
// internal
setMargin(layoutParams, internalMargin, internalMargin);
}
}
}
tabLayout.requestLayout();
}
}
private void setMargin(ViewGroup.MarginLayoutParams layoutParams, int start, int end) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
layoutParams.setMarginStart(start);
layoutParams.setMarginEnd(end);
} else {
layoutParams.leftMargin = start;
layoutParams.rightMargin = end;
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow