2015-06-03 2 views
73

Я пытаюсь использовать новый проект TabLayout в своем проекте. Я хочу, чтобы макет адаптировался к каждому размеру экрана и ориентации, но его можно увидеть правильно в одной ориентации.Android Support Design TabLayout: Gravity Center и Mode Scrollable

Я имею дело с силой тяжести и настройки режима мой tabLayout как:

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

Поэтому я ожидаю, что если нет места, то tabLayout прокручивать, но если есть место, то по центру.

Из путеводителей:

общественности статической окончательный ИНТ GRAVITY_CENTER Гравитация используется раскладывать вкладки в центре TabLayout.

public static final int GRAVITY_FILL Гравитация, используемая для заполнения как можно большего количества вкладок TabLayout. Этот параметр вступает в силу только при использовании с MODE_FIXED.

public static final int MODE_FIXED Фиксированные вкладки отображают все вкладки одновременно и лучше всего использовать с контентом, который извлекает выгоду из быстрого точек между вкладками. Максимальное количество вкладок ограничено шириной представления . Фиксированные вкладки имеют равную ширину, основываясь на самой широкой вкладке .

публичных статические финальные INT MODE_SCROLLABLE Прокручиваемых вкладок отображение подмножества вкладок в любом данный момент и могут содержать более длинные метки вкладок и большее количество вкладок. Они лучше всего используются для просмотра контекстов в сенсорных интерфейсах, когда пользователям не нужно напрямую сравнивать вкладки .

Так GRAVITY_FILL совместим только с MODE_FIXED, но на это ничего для GRAVITY_CENTER не уточнил, я ожидаю, что это будет совместимо с MODE_SCROLLABLE, но это то, что я получаю, используя GRAVITY_CENTER и MODE_SCROLLABLE

enter image description here

Таким образом, он использует SCROLLABLE в обеих ориентациях, но не использует GRAVITY_CENTER.

Это то, что я ожидал бы от пейзажа; но иметь это, мне нужно установить MODE_FIXED, так что я получаю в портрете:

enter image description here

Почему GRAVITY_CENTER не работает SCROLLABLE если tabLayout подходит к экрану? Есть ли способ установить динамику и режим динамически (и посмотреть, что я ожидаю)?

спасибо!

EDITED: Это макет моего 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 вы нашли какое-либо решение этой проблемы? Я столкнулся с той же проблемой. – Spynet

+0

Единственный способ, которым я нашел, - это получить размер ваших вкладок (вручную), а затем настроить параметры макета динамически в зависимости от того, подходит оно или нет. –

+0

@ Fighter42, можете ли вы разместить образец кода для решения, которое вы используете? – Sammys

ответ

3

Как я не нашел почему это поведение произошло, я использовал следующий код:

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

В принципе, у меня есть чтобы вручную вычислить ширину моего tabLayout, а затем я установил режим Tab в зависимости от того, подходит ли tabLayout на устройстве или нет.

Причина, по которой я получаю размер макета вручную, заключается в том, что не все вкладки имеют одинаковую ширину в режиме прокрутки, и это может спровоцировать, что некоторые имена используют 2 строки, как это случилось со мной в этом примере.

+0

С помощью этого решения текст потенциально может быть сжат и с использованием двух линий. См. Другие ответы и решения в этой теме. Вы уменьшаете риск появления этого, но часто появляетесь, когда TabLayout немного меньше ширины экрана. – Sotti

87

Вкладка только гравитационные эффекты MODE_FIXED.

Одним из возможных решений является установка вашего layout_width в wrap_content и layout_gravity к 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" /> 

Если язычки меньше, чем ширина экрана, сама TabLayout также будет меньше, и он будет в центре из-за гравитация. Если вкладки больше ширины экрана, то TabLayout будет соответствовать ширине экрана, и прокрутка активируется.

+1

Я использую конструкцию материалов для своего приложения. Я следовал образцу с http://www.androidhive.info/2015/09/android-material-design-working-with-tabs/. Tab scrollable не работает в устройствах Kitkat и Jelly bean. В lollipop я могу прокручивать Tab, но в других устройствах с леденцами. Прокрутка прокрутки не работает, но салфетка работает нормально. Могу я знать, в чем проблема. – user2681579

+0

Это не сработало для меня по API 15/support.design:25.1.0. Вкладки были оправданы при ширине окна. Установка 'app: tabMaxWidth =" 0dp "и' android: layout_centerHorizontal = "true" 'работали с вкладками центра. – Baker

+0

Это сработало для меня. Вкладки всегда сосредоточены, но когда они не прокручиваются, они не заполняют всю ширину. –

7

Посмотрите на android-tablayouthelper

Автоматическое переключение TabLayout.MODE_FIXED и TabLayout.MODE_SCROLLABLE зависит от общей ширины вкладок.

+0

Но когда вкладки Titles являются динамическими. Текст идет в следующей строке. И если используется пользовательский макет для вкладки. Текст, эллипсизованный в конце, как – Nepster

4

Это решение, которое я используется для автоматического переключения между SCROLLABLE и FIXED + FILL. Это полный код для решения @ Fighter42:

(код ниже показывает, где поставить модификацию, если вы использовали шаблон вкладок деятельности компании 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); 
      } 
     } 
    }); 
} 

Планировка:

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

Если вам не нужно заполнять ширину, лучше использовать решение @karaokyo.

+0

, хорошо работает; переключение между пейзажем и портретом делает именно то, что вы можете ожидать. Google должен реализовать это в шаблоне по умолчанию для вкладок. –

+0

Этот ответ имеет те же проблемы, о которых я упомянул в решении Tiago. – Sotti

10

это как я это сделал

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

Я сделал небольшие изменения решения @Mario Веласко на работоспособных частях

+0

Это решение имеет те же проблемы, о которых я упомянул в решении Tiago. – Sotti

+0

Это решение работает на вкладке без значка, что, если у меня есть значок с текстом? :/ –

+0

@EmreTekin да, это работает со значком, у моих вкладок есть значки с текстом – Flux

1

Это единственный код, который работал для меня:

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 можно получить, используя это:

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

И ваш TabLayout XML должен выглядеть следующим образом (не забудьте установить 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

Это почти что есть, но есть некоторые проблемы.Наиболее очевидная проблема заключается в том, что в некоторых устройствах (и большинстве медленных) вы действительно увидите, как макет изменяется и изменяется. – Sotti

+1

Существует еще одна проблема, этот подход измеряет общую ширину TabLayout, чтобы решить, нужно ли прокручивать или нет. Это не очень точно. Бывают ситуации, когда TabLayout не достигает обоих краев в режиме прокрутки, но нет места для фиксированного режима без сокращения текста злоупотребления количеством строк на вкладке (обычно 2). Это прямое следствие того, что вкладки в фиксированном режиме имеют фиксированную ширину (screenWidth/numberOfTabs), а в режиме прокрутки они обматывают текст + paddings. – Sotti

4

держит вещи простыми просто добавить приложение: tabMode = "прокручивать" и андроид: layout_gravity = "снизу"

так же, как это

<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

Это не то, о чем просит создатель. Проблема с этим подходом заключается в том, что в сценариях, где у вас есть 2 вкладки или 3 три вкладки в телефонах, где текст мал, у вас будет макет, такой как Google Play Store, где вкладки находятся слева. Создатель хочет, чтобы табуляции достигали от края до края, когда это возможно, и прокручивайте в противном случае. – Sotti

2

Все, что вам нужно, это добавить следующие строки в TabLayout

custom:tabGravity="fill" 

Итак вы будете иметь:

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

Это не то, о чем просит создатель. Проблемы здесь в том, что вкладки никогда не будут прокручиваться, и вы избавитесь от пространства на экране. Вкладки начнут уменьшать текст, и в итоге они получат две выровненные. – Sotti

1

Я создал класс AdaptiveTabLayout для достижения этой цели. Это был единственный способ найти решение проблемы, ответить на вопрос и избежать/проблемы с обходом, которые нет в других ответах.

Примечания:

  • Ручки раскладок телефона/планшета.
  • Обрабатывает футляры, в которых достаточно номер для MODE_SCROLLABLE, но не хватает места для MODE_FIXED. Если вы не будете обрабатывать этот случай, это произойдет на некоторых устройствах, вы будете видеть различные размеры текста или печь две строки текста на некоторых вкладках, которые выглядит плохо.
  • Он принимает реальные меры и не делает никаких предположений (например, экран шириной 360dp или что-то еще ...). Это работает с реальными размерами экрана и реальными размерами вкладок. Это означает, что он хорошо работает с переводами, потому что не предполагает какого-либо размера вкладок, вкладки получают меру.
  • Сделки с различными проходами на фазе onLayout, чтобы избежать дополнительной работы.
  • Ширина макета должна быть wrap_content на xml. Не устанавливайте режим или силу тяжести на 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; 
    } 

} 

И это класс 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; 
    } 

} 

Используйте пример:

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

Проблемы/обратиться за помощью:

  • Вы увидите это:

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 

Я не знаю, как ее решить. Какие-либо предложения?

  • Чтобы сделать ребенок меру TabLayout, я делаю некоторые отливки и предположение (как ребенок является LinearLayout, содержащего другие взгляды ....) Это может вызвать проблемы с в дальнейших обновлениях библиотеки поддержки Design. Лучший подход/предложения?
0

Я решил эту проблему с использованием следующего

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

    } 
Смежные вопросы