resueltos - Diseño de la pestaña de Android: ajuste el ancho del indicador de pestaña con respecto al título de la pestaña
A partir de la biblioteca de soporte 28 puede hacer lo siguiente:
app:tabIndicatorFullWidth="false"
app:tabPaddingStart="25dp"
app:tabPaddingEnd="25dp"
Puede configurar el relleno deseado que afecta al indicador de pestaña.
También ahora puedes hacer esto:
app:tabIndicator="@drawable/tab_indicator"
esto establecerá un dibujo personalizable como indicador.
Ejemplo de dibujo personalizable:
<?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>
Ajustar el ancho del indicador de pestaña con respecto al título de la pestaña y agregar el relleno derecho con el título de la pestaña y el margen derecho con el indicador de pestaña.
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;
}
}
}
Estoy seguro de la versión SDK necesaria, pero puede hacerlo directamente en su archivo XML, agregando el atributo de relleno usando el espacio de nombres de la aplicación.
Solo haz que tu TabLayout se vea así:
<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>
Puedes mover los xmlns:app="http://schemas.android.com/apk/res-auto"
para tu padre también, pero es tu elección :)
Gracias, leon por su respuesta, que me llevó a lograr eso mediante XML.
Hay un atributo llamado tabIndicatorFullWidth dentro de 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" />
Intenté 2 horas para varias soluciones, pero ninguna de ellas tiene un efecto perfecto.
El problema radica en el relleno del tabView, incluso si configuro su relleno en todo 0,
Las distintas pestañas tienen un relleno diferente y, por lo tanto, el tamaño del texto dentro de su TextView
varía.
Entonces encontré esta biblioteca que la resolvió perfectamente.
https://github.com/H07000223/FlycoTabLayout
Como dijo @Stas Melnychenko, probablemente no podamos hacerlo a menos que reescribamos un TabLayout
. Y esta biblioteca vuelve a escribir un TabLayout
...
La respuesta corta es "no". Aquí está la explicación.
Hay una clase privada SlidingTabStrip
dentro de TabLayout que dibuja el indicador
@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);
}
}
Creo que mIndicatorLeft y mIndicatorRight es lo que necesita. Estos campos se establecen en la misma clase:
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);
}
}
donde los parámetros izquierdo y derecho se calculan en el siguiente método:
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);
}
Y lo peor es que el campo SlidingTabStrip en TabLayout es privado y final.
private final SlidingTabStrip mTabStrip;
No veo cómo es posible lograr lo que necesita sin crear TabLayout totalmente nuevo.
Puedes hacerlo usando estilos.
En el diseño xml:
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
style="@style/AppTabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
en el styles.xml:
<style name="AppTabLayout" parent="Widget.Design.TabLayout">
<item name="android:layout_marginLeft">40dp</item>
<item name="android:layout_marginRight">40dp</item>
</style>
Una solución simple es:
tabLayout.setTabIndicatorFullWidth(false);
Pero funciona con la última dependencia como
implementation ''com.android.support.constraint:constraint-layout:1.1.2''
implementation ''com.android.support:design:28.0.0''
Si no necesita que la tira sea más pequeña que el texto, esto debería funcionar:
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();
}
}
Para los puntos agregados puedes verificar el tamaño del texto de cada título.
Sí , es posible ajustar el indicador de tabulación como relleno de configuración de título a 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;
}
}
EDITAR : A partir de com.android.support:design:28.0.0
, puede ajustar el indicador como etiqueta fácilmente ahora:
app:tabIndicatorFullWidth="false"