2015-06-03 20 views
73

Próbuję użyć nowego projektu TabLayout w moim projekcie. Chcę, aby układ dostosowywał się do każdego rozmiaru i orientacji ekranu, ale można go zobaczyć poprawnie w jednej orientacji.TabLayout Android Support Design: Centrum Gravity i tryb Przewijany

mam do czynienia z grawitacją i ustawienie trybu mój tabLayout jak:

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

więc spodziewać, że jeśli nie ma pomieszczenia, tabLayout jest przewijane, ale jeśli nie ma miejsca, to jest wyśrodkowany.

z prowadnic:

public static final int GRAVITY_CENTER Gravity wykorzystywane do ułożyć karty w środku TabLayout.

public static final int GRAVITY_FILL Grawitacja używana do wypełnienia TabLayout tak bardzo, jak to możliwe. Ta opcja działa tylko wtedy, gdy używana jest funkcja MODE_FIXED.

public static final int MODE_FIXED Stałe zakładki wyświetlają jednocześnie wszystkie karty i są najlepiej używane z treścią, która korzysta z szybkiej przestawiania między kartami. Maksymalna liczba zakładek jest ograniczona przez szerokość widoku . Naprawiono zakładki mają jednakową szerokość, w oparciu o najszerszą etykietę zakładki.

public static final int MODE_SCROLLABLE Przewijane zakładki wyświetlają w każdej chwili podzbiór kart w danym momencie i mogą zawierać dłuższe etykiety zakładek i większą liczbę zakładek. Są najlepiej używane do kontekstów przeglądania w interfejsach dotykowych, gdy użytkownicy nie muszą bezpośrednio porównywać etykiet tabulacji .

Więc GRAVITY_FILL jest kompatybilny tylko z MODE_FIXED ale co znaczy nie określa coś dla GRAVITY_CENTER, spodziewam się, że jest zgodny z MODE_SCROLLABLE, ale to jest to, co się za pomocą GRAVITY_CENTER i MODE_SCROLLABLE

enter image description here

Wykorzystuje więc PRZEWIJANIE w obu orientacjach, ale nie używa GRAVITY_CENTER.

Tego oczekuję w krajobrazie; ale mieć to, muszę ustawić MODE_FIXED, więc co mam w układzie pionowym jest:

enter image description here

Dlaczego GRAVITY_CENTER nie pracuje dla przewijalnych jeśli tabLayout pasuje do ekranu? Czy istnieje sposób dynamicznego ustawiania grawitacji i trybu (i zobaczenia, czego się spodziewam)?

Dziękuję bardzo!

edycja: Jest to układ z moim 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" /> 
+0

@ Fighter42 pan znaleźć żadnego rozwiązania tego? Stoję w obliczu tego samego problemu. – Spynet

+0

Jedynym sposobem, jaki znalazłem, było pobranie rozmiaru kart (ręcznie), a następnie skonfigurowanie parametrów układu w sposób dinamiczny, w zależności od tego, czy pasuje, czy nie. –

+0

@ Fighter42, czy możesz opublikować przykładowy kod dla rozwiązania, którego używasz? – Sammys

Odpowiedz

3

Jak nie znaleźliśmy dlaczego takie zachowanie zdarzyć Użyłem następujący kod:

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

Zasadniczo mam obliczyć ręcznie szerokość mojego tabLayout, a następnie ustawić tryb Tab w zależności od tego, czy tabLayout pasuje do urządzenia czy nie.

Powód, dla którego ręczne pobranie rozmiaru układu jest spowodowane tym, że nie wszystkie zakładki mają tę samą szerokość w trybie Przewijany, a to może spowodować, że niektóre nazwiska używają 2 wierszy, tak jak przydarzyło się to w przykładzie.

+0

Dzięki temu rozwiązaniu tekst mógłby zostać skrócony i również za pomocą 2 linii. Zobacz inne odpowiedzi i rozwiązania w tym wątku na ten temat. Redukujesz ryzyko tego pojawiania się, ale pojawi się często tak czy inaczej, gdy TabLayout jest nieco mniejszy niż ScreenWidth. – Sotti

87

Efekty grawitacyjne tabulatora MODE_FIXED.

Możliwym rozwiązaniem jest ustawienie w layout_width do wrap_content i layout_gravity do 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" /> 

Jeśli zaczepy są mniejsze niż szerokość ekranu, sama TabLayout będzie również mniejszy i będzie się koncentrować powodu grawitacja. Jeśli zakładki są większe niż szerokość ekranu, TabLayout dopasuje szerokość ekranu i przewijanie zostanie aktywowane.

+1

Używam Material Design dla mojej aplikacji. Śledziłem próbkę z http://www.androidhive.info/2015/09/android-material-design-working-with-tabs/ Przewijanie z zakładkami nie działa w urządzeniach typu bean Kitkat i Jelly. W Lollipop mogę przewijać Tab, ale w innych urządzeniach niż Lollipop przewijanie Tab nie działa, ale machnięcie działa dobrze. Czy mogę wiedzieć, jaki byłby problem. – user2681579

+0

Nie działa to dla mnie na API 15/support.design:25.1.0. Tabulatory zostały wyrównane do lewej, gdy są węższe niż szerokość okna. Ustawienie 'app: tabMaxWidth =" 0dp "' i 'android: layout_centerHorizontal =" true "' działało na środkowych kartach. – Baker

+0

To zadziałało dla mnie. Tabulatory są zawsze wyśrodkowane, ale gdy nie są faktycznie przewijane, nie wypełniają całej szerokości. –

7

Spójrz na android-tablayouthelper

Automatyczne przełączanie TabLayout.MODE_FIXED i TabLayout.MODE_SCROLLABLE zależy od całkowitej szerokości zakładki.

+0

Ale gdy tytuły karty są dynamiczne. Tekst przechodzi do następnej linii. I jeśli użyjesz niestandardowego układu dla karty. Elipsa tekstowa na końcu jak – Nepster

4

To rozwiązanie, które stosuje się do automatycznego przełączania między SCROLLABLE i FIXED + FILL. Jest to kompletny kod dla roztworu @ Fighter42:

(Poniższy kod pokazuje, gdzie umieścić modyfikację jeśli już używany szablon aktywności kartach 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); 
      } 
     } 
    }); 
} 

Układ:

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

Jeśli nie musisz wypełniać szerokości, lepiej użyć rozwiązania @karaokyo.

+0

to działa dobrze; przełączanie między krajobrazem a portretem wykonuje dokładnie to, czego możesz się spodziewać. Google powinno to zaimplementować w domyślnym szablonie aktywności związanej z kartami. –

+0

Ta odpowiedź ma te same problemy, o których wspomniałem w rozwiązaniu Tiago. – Sotti

10

Tak to zrobiłem

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

Zrobiłem małe zmiany roztworu @Mario Velasco ze strony runnable

+0

To rozwiązanie ma te same problemy, o których wspomniałem w rozwiązaniu Tiago. – Sotti

+0

To rozwiązanie działa na karcie bez ikony, co jeśli mam ikonę z tekstem ?? :/ –

+0

@EmreTekin Tak, to działa z ikoną, moje karty mają ikony z tekstem – Flux

1

To jest tylko kod, który pracował dla mnie:

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

DisplayMetrics można pobrać za pomocą:

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

A twój TabLayout XML powinien wyglądać następująco (nie zapomnij ustawić tabMaxWidth 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

To prawie jest, ale są pewne problemy.Najbardziej oczywistym problemem jest to, że w niektórych urządzeniach (i większości powolnych) widać, jak układ jest modyfikowany i zmieniany. – Sotti

+1

Jest inny problem, to podejście mierzy całkowitą szerokość TabLayout, aby zdecydować, czy można przewijać, czy nie. To nie jest zbyt dokładne. Są sytuacje, w których TabLayout nie osiąga obu krawędzi podczas trybu przewijania, ale nie ma wystarczająco miejsca dla trybu stałego bez zmniejszania tekstu nadużywania liczby linii na karcie (zwykle 2). Jest to bezpośrednią konsekwencją tego, że zakładki w trybie stałym mają stałą szerokość (screenWidth/numberOfTabs), podczas gdy w trybie przewijania owijają tekst + paddings. – Sotti

4

utrzymuje rzeczy proste, wystarczy dodać aplikację: tabMode = "przewijania" i Android: layout_gravity = "bottom"

podobnie jak ten

<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

+3

Nie o to pyta twórca. Problem z tym podejściem polega na tym, że w scenariuszach, w których masz 2 zakładki lub 3 trzy zakładki na telefonach, gdzie tekst jest mały, będziesz mieć układ podobny do Sklepu Google Play, w którym karty znajdują się po lewej stronie. Twórca chce, aby zakładki sięgały od krawędzi do krawędzi, gdy jest to możliwe, i przewijać w przeciwnym razie. – Sotti

2

Wystarczy dodać następujące wpisy do TabLayout

custom:tabGravity="fill" 

Więc musisz:

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

Nie o to pyta twórca. Problem polega na tym, że zakładki nigdy nie będą przewijane, a pozbędziesz się miejsca na ekranie. Zakładki zaczną zmniejszać tekst, a ostatecznie dostaną dwie linie. – Sotti

1

Stworzyłem klasę AdaptiveTabLayout do osiągnięcia tego celu. To był jedyny sposób, w jaki znalazłem rozwiązanie problemu, odpowiedź na pytanie i unikanie/obejście problemów, których inne odpowiedzi tutaj nie dotyczą.

Uwagi:

  • Uchwyty układy telefonu/tabletu.
  • Zajmuje się przypadkami, w których jest wystarczająco dużo miejsca na dla MODE_SCROLLABLE, ale za mało miejsca dla MODE_FIXED. Jeśli nie zajmiesz się tym przypadkiem, stanie się to na niektórych urządzeniach, na których zobaczysz różne rozmiary tekstu lub dwie linijki tekstu w niektórych zakładkach, które wyglądają źle.
  • Otrzymuje prawdziwe miary i nie przyjmuje żadnych założeń (np. Ekran ma szerokość 360dp lub cokolwiek ...). Działa to z rzeczywistymi rozmiarami ekranu i rzeczywistymi rozmiarami kart. Oznacza to, że działa dobrze z tłumaczeniami, ponieważ nie przyjmuje żadnego rozmiaru tabulatora, taby otrzymują miarę.
  • Umożliwia obejście różnych etapów fazy onLayout w celu uzyskania uniknięcia dodatkowej pracy.
  • Szerokość układu musi wynosić wrap_content w pliku xml. Nie ustawiaj żadnego trybu ani grawitacji na pliku 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; 
    } 

} 

I to jest klasa 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; 
    } 

} 

Przykład zastosowania:

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

Problemy/prosić o pomoc:

  • Zobaczysz to:

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 

Nie jestem pewien, jak go rozwiązać. Jakieś sugestie?

  • Aby środki podrzędne TabLayout, robię jakieś odlewy i założeń (jak dziecko jest LinearLayout zawierająca inne widoki ....) może to spowodować problemy z W kolejnych aktualizacjach Projekt Biblioteki Pomoc. Lepsze podejście/sugestie?
0

I rozwiązać to za pomocą następujących

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

    }