Android支持deviseTabLayout:重力中心和模式可滚动

我正在尝试在我的项目中使用新的Design TabLayout。 我希望布局适应每个屏幕大小和方向,但是可以在一个方向上正确地看到它。

我正在处理重力和模式设置我的tabLayout为:

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

所以我期望如果没有空间,tabLayout是可滚动的,但是如果有空间的话,它是居中的。

从指南:

公共静态最终诠释GRAVITY_CENTER重力用于布局在TabLayout中心的标签。

public static final int GRAVITY_FILL重力用于尽可能地填充TabLayout。 此选项仅在与MODE_FIXED一起使用时才生效。

公共静态最终诠释MODE_FIXED固定选项卡并发显示所有标签,最适用于从标签之间的快速枢轴受益的内容。 选项卡的最大数量受限于视图的宽度。 固定标签具有相同的宽度,基于最宽的标签标签。

public static final int MODE_SCROLLABLE可滚动选项卡在任何给定时刻显示选项卡的子集,并且可以包含更长的选项卡标签和更多数量的选项卡。 当用户不需要直接比较标签标签时,它们最适合用于在触摸界面中浏览上下文。

所以GRAVITY_FILL只与MODE_FIXED兼容,但是在没有为GRAVITY_CENTER指定任何东西,我期望它与MODE_SCROLLABLE兼容,但这是我使用GRAVITY_CENTER和MODE_SCROLLABLE

在这里输入图像说明

所以它在两个方向都使用SCROLLABLE,但它不使用GRAVITY_CENTER。

这是我期望的景观。 但有这个,我需要设置MODE_FIXED,所以我得到的肖像是:

在这里输入图像说明

为什么如果tabLayout适合屏幕,GRAVITY_CENTER不能用于SCROLLABLE? 有没有什么办法来dynamic设置重力和模式(并看看我的期望)?

非常感谢你!

编辑:这是我的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" /> 

标签重力只影响MODE_FIXED

一种可能的解决scheme是将layout_width设置为wrap_contentlayout_gravitycenter_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将与屏幕宽度匹配,并且滚动将被激活。

这是我如何做到的

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 Velasco的解决scheme做了小小的修改

看看android-tablayouthelper

自动切换TabLayout.MODE_FIXED和TabLayout.MODE_SCROLLABLE取决于总的标签宽度。

保持简单只需添加app:tabMode =“scrollable”和android:layout_gravity =“bottom”

像这样

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

在这里输入图像说明

这是我用来自动更改SCROLLABLEFIXED + FILL的解决scheme。 这是@ Fighter42解决scheme的完整代码:

(如果您使用了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解决scheme。

所有你需要的是将以下添加到您的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" /> 

因为我没有find为什么会发生这种行为我已经使用了下面的代码:

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

基本上,我必须手动计算我的tabLayout的宽度,然后根据tabLayout是否适合在设备中设置选项卡模式。

为什么我手动获取布局大小的原因是因为并非所有选项卡在可滚动模式下都具有相同的宽度,并且这可能会引起某些名称使用2行,正如在示例中发生的那样。

这是唯一对我有用的代码:

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

我创build了一个AdaptiveTabLayout类来实现这一点。 这是我发现真正解决问题的唯一方法,回答这个问题,并避免/解决其他答案在这里没有解决的问题。

笔记:

  • 处理手机/平板电脑布局。
  • 处理MODE_SCROLLABLE有足够空间但MODE_FIXED空间MODE_FIXED 。 如果你不处理这种情况,它会发生在一些设备上,你会看到不同的文本大小或在一些标签中烤箱两行文本,这看起来不好。
  • 它得到真正的措施,并没有作出任何假设(如屏幕是360dp宽或任何…)。 这适用于真正的屏幕尺寸和真正的标签尺寸。 这意味着与翻译效果很好,因为不承担任何标签大小,标签得到措施。
  • 在onLayout阶段处理不同的通行证,以避免额外的工作。
  • 布局宽度需要在xml上是wrap_content 。 不要在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> 

要旨

问题/寻求帮助:

  • 你会看到这个:

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 

我不知道如何解决它。 有什么build议么?

  • 为了使TabLayout子项的措施,我做了一些铸件和假设(像孩子是一个LinearLayout包含其他意见….)这可能会导致进一步的devise支持库更新的问题。 更好的方法/build议?

我解决了这个使用以下

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