webentwicklung-frage-antwort-db.com.de

TabLayout für Android-Support-Design: Gravity Center und Modus scrollbar

Ich versuche, das neue Design TabLayout in meinem Projekt zu verwenden. Ich möchte, dass sich das Layout an jede Bildschirmgröße und Ausrichtung anpasst, es kann jedoch in einer Ausrichtung richtig angezeigt werden.

Ich beschäftige mich mit Schwerkraft und Modus und setze mein tabLayout wie folgt:

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

Ich gehe also davon aus, dass das tabLayout scrollbar ist, wenn kein Platz vorhanden ist. Wenn jedoch Platz vorhanden ist, wird es zentriert.

Von den Führern: 

public static final int GRAVITY_CENTER Die Schwerkraft wird verwendet, um die Registerkarten in der Mitte des TabLayout.

public static final int GRAVITY_FILL Schwerkraft, mit der die .__ gefüllt wird. TabLayout so viel wie möglich. Diese Option wird nur wirksam, wenn sie verwendet wird mit MODE_FIXED.

public static final int MODE_FIXED Feste Registerkarten zeigen alle Registerkarten an gleichzeitig und werden am besten mit Inhalten verwendet, die von quick profitieren schwenkt zwischen den Laschen. Die maximale Anzahl der Registerkarten ist durch die .__ begrenzt. Breite der Ansicht. Feste Registerkarten haben dieselbe Breite, basierend auf der breitesten Registerkarte Etikette.

public static final int MODE_SCROLLABLE Rollbare Registerkarten zeigen eine .__ an. Untermenge von Registerkarten zu einem bestimmten Zeitpunkt und kann längere Registerkartenbezeichnungen enthalten und eine größere Anzahl von Registerkarten. Sie werden am besten zum Durchsuchen von Kontexten verwendet in Touch-Oberflächen, wenn Benutzer die Registerkarte nicht direkt vergleichen müssen Etiketten.

GRAVITY_FILL ist also nur mit MODE_FIXED kompatibel, aber bei ist nichts für GRAVITY_CENTER angegeben. Ich erwarte, dass es mit MODE_SCROLLABLE kompatibel ist. Dies ist jedoch, was ich mit GRAVITY_CENTER und MODE_SCROLLABLE bekomme

enter image description here

Es wird also SCROLLABLE in beiden Ausrichtungen verwendet, jedoch nicht GRAVITY_CENTER.

Das würde ich für Landschaft erwarten; aber um dies zu haben, muss ich MODE_FIXED setzen, also bekomme ich im Portrait:

enter image description here

Warum funktioniert GRAVITY_CENTER nicht für SCROLLABLE, wenn das tabLayout auf den Bildschirm passt? Gibt es eine Möglichkeit, die Schwerkraft und den Modus dynamisch einzustellen (und zu sehen, was ich erwarte)?

Vielen Dank!

BEARBEITET: Dies ist das Layout meines TabLayouts:

<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

Da ich nicht gefunden habe, warum dieses Verhalten auftritt, habe ich folgenden Code verwendet:

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

Grundsätzlich muss ich die Breite meines tabLayout manuell berechnen und dann den Tab-Modus einstellen, je nachdem, ob das tabLayout in das Gerät passt oder nicht.

Der Grund, warum ich die Größe des Layouts manuell erhalte, ist, dass nicht alle Registerkarten im scrollbaren Modus die gleiche Breite haben. Dies könnte dazu führen, dass einige Namen zwei Zeilen verwenden, wie es mir im Beispiel passiert ist.

7
Javier Delgado

Die Schwerkraft der Registerkarte wirkt sich nur auf MODE_FIXED aus.

Eine mögliche Lösung besteht darin, Ihren layout_width auf wrap_content und layout_gravity auf center_horizontal zu setzen:

<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" />

Wenn die Registerkarten kleiner als die Bildschirmbreite sind, ist auch die Variable TabLayout kleiner und aufgrund der Schwerkraft zentriert. Wenn die Registerkarten größer als die Bildschirmbreite sind, stimmt die TabLayout mit der Bildschirmbreite überein und der Bildlauf wird aktiviert.

105
tachyonflux

so habe ich es gemacht

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

Ich habe kleine Änderungen an der Lösung von @Mario Velasco im laufenden Bereich vorgenommen

13
Flux

Schau dir Android-Tablayouthelper an

Wechseln Sie automatisch zwischen TabLayout.MODE_FIXED und TabLayout.MODE_SCROLLABLE hängt von der Gesamtbreite der Registerkarten ab.

7
Derek Beattie

die Dinge bleiben einfach: Fügen Sie die App hinzu: tabMode = "scrollbar"

genau wie dieser 

<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

Dies ist die Lösung, mit der ich automatisch zwischen SCROLLABLE und FIXED + FILL gewechselt habe. Es ist der vollständige Code für die @ Fighter42-Lösung:

(Der folgende Code zeigt, wo die Änderung eingefügt werden soll, wenn Sie die Aktivitätsvorlage mit Registern von Google verwendet haben.)

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

Layout:

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

Wenn Sie die Breite nicht ausfüllen müssen, sollten Sie die @karaokyo-Lösung verwenden.

4
Mario Velasco

Dafür habe ich eine AdaptiveTabLayout-Klasse erstellt. Dies war die einzige Möglichkeit, das Problem tatsächlich zu lösen, die Frage zu beantworten und Probleme zu vermeiden/zu umgehen, die andere Antworten hier nicht bieten.

Anmerkungen:

  • Für Telefon-/Tablet-Layouts.
  • Behandelt Fälle, in denen genügend Platz vorhanden istZimmer für MODE_SCROLLABLE, aber nicht genügend Platz für MODE_FIXED. Wenn Sie mit diesem Fall nicht umgehen, wird dies auf einigen Geräten passieren. Sie werden unterschiedliche Textgrößen sehen oder zwei Textzeilen in einigen Registerkarten erstellen, die schlecht aussehen.
  • Es bekommt echte Maße und macht keine Annahmen (wie der Bildschirm ist 360dp breit oder was auch immer ...). Dies funktioniert mit echten Bildschirmgrößen und echten Tab-Größen. Dies bedeutet, dass es bei Übersetzungen gut funktioniert, da keine Tabulatorgröße angenommen wird. Die Registerkarten erhalten Maß.
  • Behandelt die verschiedenen Durchläufe in der OnLayout-Phase, um Zusätzliche Arbeit zu vermeiden.
  • Die Layoutbreite muss wrap_content in der XML-Datei sein. Stellen Sie keinen Modus oder die Schwerkraft in der XML ein.

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

}

Und das ist die DeviceUtils-Klasse:

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

}

Beispiel verwenden:

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

Kern

Probleme/Bitte um Hilfe:

  • Sie werden das sehen:

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

Ich bin nicht sicher, wie ich es lösen kann. Irgendwelche Vorschläge?

  • Um das TabLayout-Element für untergeordnete Elemente festzulegen, mache ich einige Castings und Annahmen (wie das untergeordnete Element ist ein LinearLayout, das andere Ansichten enthält ...) Dies kann zu Problemen mit weiteren Aktualisierungen der Design Support Library führen. Ein besserer Ansatz/Vorschläge?
3
Sotti

Dies ist der einzige Code, der für mich funktioniert hat:

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

Die DisplayMetrics kann folgendermaßen abgerufen werden:

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

Und Ihr TabLayout-XML sollte folgendermaßen aussehen (vergessen Sie nicht, tabMaxWidth auf 0 zu setzen):

<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

Ich habe das mit folgendem gelöst

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

    }
1
zohaib khaliq

Alles, was Sie brauchen, ist, Ihrem TabLayout folgendes hinzuzufügen

custom:tabGravity="fill"

So haben Sie dann:

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

Sehr einfaches Beispiel und es funktioniert immer.

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

Meine endgültige Lösung

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