web-dev-qa-db-fra.com

Onglet Conception du support Android: Centre de gravité et mode de défilement

J'essaie d'utiliser le nouveau Design TabLayout dans mon projet. Je veux que la disposition s'adapte à toutes les tailles et orientations d'écran, mais elle peut être vue correctement dans une orientation.

Je traite avec Gravity et Mode en réglant mon tabLayout comme:

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

Donc, je suppose que s'il n'y a pas de place, l'onglet tabLayout est déroulable, mais s'il y a de la place, il est centré.

Des guides: 

public static final int GRAVITY_CENTER Gravity avait l'habitude de disposer le onglets au centre de TabLayout.

public static final int GRAVITY_FILL Gravité utilisée pour remplir le fichier TabLayout autant que possible. Cette option ne prend effet que lorsqu'elle est utilisée avec MODE_FIXED.

public static final int MODE_FIXED Les onglets fixes affichent tous les onglets en même temps et sont mieux utilisés avec un contenu qui profite de quick pivote entre les onglets. Le nombre maximal d'onglets est limité par le la largeur de la vue. Les onglets fixes ont une largeur égale, basée sur l'onglet le plus large étiquette.

public static final int MODE_SCROLLABLE Les onglets déroulants affichent un sous-ensemble d'onglets à tout moment, et peut contenir des étiquettes d'onglets plus longues et un plus grand nombre d'onglets. Ils sont mieux utilisés pour les contextes de navigation interfaces tactiles lorsque les utilisateurs n'ont pas besoin de comparer directement l'onglet Étiquettes.

Donc, GRAVITY_FILL est compatible uniquement avec MODE_FIXED, mais, cela ne spécifie rien pour GRAVITY_CENTER, je l’attends pour être compatible avec MODE_SCROLLABLE, mais c’est ce que je reçois avec GRAVITY_CENTER et MODE_SCROLLABLE.

enter image description here

Donc, il utilise SCROLLABLE dans les deux orientations, mais pas GRAVITY_CENTER.

C'est ce que j'attendrais du paysage. mais pour avoir cela, je dois définir MODE_FIXED, donc ce que je reçois en portrait est:

enter image description here

Pourquoi GRAVITY_CENTER ne fonctionne-t-il pas avec SCROLLABLE si l'onglet tabLayout convient à l'écran? Existe-t-il un moyen de régler la gravité et le mode de manière dynamique (et de voir ce que j'attends)?

Merci beaucoup!

EDITED: Ceci est la mise en page de mon TabLayout:

<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" />
82
Javier Delgado

Comme je n'ai pas trouvé pourquoi ce problème se produit, j'ai utilisé le code suivant:

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

Fondamentalement, je dois calculer manuellement la largeur de mon tabLayout, puis je règle le mode de tabulation en fonction de l'adéquation du tabLayout à l'appareil.

J'obtiens manuellement la taille de la présentation parce que tous les onglets n'ont pas la même largeur en mode Scrollable, ce qui peut provoquer que certains noms utilisent 2 lignes comme cela m'est arrivé dans l'exemple.

7
Javier Delgado

La gravité par tabulation n’affecte que MODE_FIXED.

Une solution possible consiste à définir votre layout_width sur wrap_content et votre layout_gravity sur 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" />

Si les onglets sont plus petits que la largeur de l'écran, la TabLayout sera elle aussi plus petite et centrée à cause de la gravité. Si les onglets sont plus grands que la largeur de l'écran, TabLayout correspond à la largeur de l'écran et le défilement est activé.

105
tachyonflux

voici comment je l'ai fait

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);
            }
        }
    };

J'ai légèrement modifié la solution de @Mario Velasco sur la partie exécutable

13
Flux

Regardez Android-tablayouthelper

Basculez automatiquement TabLayout.MODE_FIXED et TabLayout.MODE_SCROLLABLE dépend de la largeur totale de l'onglet.

7
Derek Beattie

garde les choses simples il suffit d'ajouter app: tabMode = "scrollable" et Android: layout_gravity = "bottom"

juste comme ça 

<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
Rajesh Kumar

C’est la solution que j’ai utilisée pour changer automatiquement entre SCROLLABLE et FIXED + FILL. C'est le code complet de la solution @ Fighter42:

(Le code ci-dessous montre où placer la modification si vous avez utilisé le modèle d'activité à onglets de Google)

@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's 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);
            }
        }
    });
}

Disposition:

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

Si vous n'avez pas besoin de remplir la largeur, utilisez de préférence la solution @karaokyo.

4
Mario Velasco

J'ai créé une classe AdaptiveTabLayout pour y parvenir. C’est le seul moyen que j’ai trouvé de résoudre le problème, de répondre à la question et d’éviter/de résoudre les problèmes qui ne sont pas résolus.

Remarques:

  • Gère les dispositions de téléphone/tablette.
  • Traite les cas où il y a assez salle pour MODE_SCROLLABLE mais pas assez d'espace pour MODE_FIXED. Si Vous ne gérez pas ce cas, cela se produira sur certains appareils. Vous verrez alors différentes tailles de texte ou ferez glisser deux lignes de texte dans certains onglets, ce qui paraît mauvais.
  • Il obtient de vraies mesures et ne fait aucune hypothèse (comme l’écran est large de 360dp ou autre chose…). Cela fonctionne avec les tailles d'écran réelles et les tailles d'onglets réels. Cela signifie que cela fonctionne bien avec les traductions, car les tailles ne sont pas mesurées.
  • Traite les différentes passes de la phase onLayout afin d'éviter tout travail supplémentaire.
  • La largeur de la mise en page doit être wrap_content sur le xml. Ne définissez aucun mode ou gravité sur le 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;
    }

}

Et voici la classe DeviceUtils:

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;
    }

}

Exemple d'utilisation:

<?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>

Essentiel

Problèmes/Demander de l'aide:

  • Vous verrez ceci:

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

Je ne sais pas comment le résoudre. Aucune suggestion?

  • Pour créer les mesures enfant TabLayout, je formule des transtypages et des hypothèses (comme l'enfant est un objet LinearLayout contenant d'autres vues ....) Cela pourrait poser problème dans les mises à jour ultérieures de Design Support Library. Une meilleure approche/suggestions?
3
Sotti

C'est le seul code qui a fonctionné pour moi:

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));
        }
    });
}

Les DisplayMetrics peuvent être récupérés en utilisant ceci:

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;
}

Et votre XML TabLayout devrait ressembler à ceci (n'oubliez pas de définir tabMaxWidth sur 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
Tiago

J'ai résolu ceci en utilisant ce qui suit

if(tabLayout_chemistCategory.getTabCount()<4)
    {
        tabLayout_chemistCategory.setTabGravity(TabLayout.GRAVITY_FILL);
    }else
    {
        tabLayout_chemistCategory.setTabMode(TabLayout.MODE_SCROLLABLE);

    }
1
zohaib khaliq

Tout ce dont vous avez besoin est d’ajouter ce qui suit à votre TabLayout

custom:tabGravity="fill"

Alors vous aurez:

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
RicNjesh

Exemple très simple et ça marche toujours.

/**
 * 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
LordTAO
 <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
user3205930

Ma solution finale

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
user4097210