我正在尝试在我的项目中使用新的Design TabLayout。我希望布局适应每个屏幕尺寸和方向,但可以在一个方向上正确看到。
我正在处理重力和模式设置我的tabLayout为:
tabLayout.setTabGravity(TabLayout.GRAVITY_CENTER);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
所以我希望如果没有空间,tabLayout是可滚动的,但如果有空间,它就会居中。
从指南中:
public static final int GRAVITY_CENTER Gravity用于布局 TabLayout中心的标签。
public static final int GRAVITY_FILL Gravity用于填充 TabLayout尽可能多。此选项仅在使用时生效 使用MODE_FIXED。
public static final int MODE_FIXED固定标签显示所有标签 同时并且最好与快速受益的内容一起使用 标签之间的枢轴。选项卡的最大数量受限于 视图的宽度。固定标签具有相同的宽度,基于最宽的标签 标签
public static final int MODE_SCROLLABLE可滚动选项卡显示a 任何给定时刻的标签子集,并且可以包含更长的标签标签 和更多的选项卡。它们最适合用于浏览上下文 当用户不需要直接比较选项卡时,在触摸界面中 标签
所以GRAVITY_FILL只与MODE_FIXED兼容,但是,如果没有为GRAVITY_CENTER指定任何内容,我希望它与MODE_SCROLLABLE兼容,但这是我使用GRAVITY_CENTER和MODE_SCROLLABLE
所以它在两个方向都使用了SCROLLABLE,但它没有使用GRAVITY_CENTER。
这是我对景观的期望;但要拥有这个,我需要设置MODE_FIXED,所以我得到的是肖像:
如果tabLayout适合屏幕,为什么GRAVITY_CENTER不能用于SCROLLABLE? 有没有办法动态设置重力和模式(并看看我期待什么)?
非常感谢!
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 :(得分:107)
Tab重力仅影响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 :(得分:13)
这就是我做的方式
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的解决方案做了一些小改动答案 2 :(得分:7)
由于我没有找到为什么会发生这种情况,我使用了以下代码:
float myTabLayoutSize = 360;
if (DeviceInfo.getWidthDP(this) >= myTabLayoutSize ){
tabLayout.setTabMode(TabLayout.MODE_FIXED);
} else {
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
}
基本上,我必须手动计算tabLayout的宽度,然后根据tabLayout是否适合设备来设置Tab Mode。
我手动获取布局大小的原因是因为并非所有选项卡在Scrollable模式下都具有相同的宽度,这可能会引发一些名称使用2行,因为它在示例中发生在我身上。
答案 3 :(得分:7)
自动切换TabLayout.MODE_FIXED和 TabLayout.MODE_SCROLLABLE取决于总标签宽度。
答案 4 :(得分:6)
保持简单只需添加app:tabMode =&#34;可滚动&#34; 和android:layout_gravity =&#34; bottom&#34;
就像这样
<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" />
答案 5 :(得分: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解决方案。
答案 6 :(得分:3)
我创建了一个AdaptiveTabLayout类来实现这一目标。这是我找到实际解决问题的唯一方法,回答问题并避免/解决其他答案不存在的问题。
注意:
MODE_SCROLLABLE
的空间,但MODE_FIXED
空间不足。如果
你不处理这种情况会发生在某些设备上你会发生的事情
在某些标签中查看不同的文字大小或烤箱两行文字
看起来很糟糕。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
我不确定如何解决它。有什么建议吗?
答案 7 :(得分: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"/>
答案 8 :(得分:1)
您只需将以下内容添加到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"
/>
答案 9 :(得分:1)
我认为更好的方法是设置 app:tabMode="auto"
和 app:tabGravity="fill"
因为将 tabMode 设置为 fixed 会使标题拥挤并导致标题在另一侧占据多行,将其设置为可滚动可能会使它们在某些屏幕尺寸的末尾留出空间。手动设置 tabMode 在处理多个屏幕尺寸时会出现问题
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
app:tabGravity="fill"
android:textAlignment="center"
app:tabMode="auto"
/>
答案 10 :(得分:0)
<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"/>
答案 11 :(得分:0)
非常简单的示例,它始终有效。
/**
* 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);
});
}
答案 12 :(得分:0)
我的最终解决方案
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
}
}
}
答案 13 :(得分:0)
在 tablayout 中添加标签时,在您的活动中添加此行
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
答案 14 :(得分:0)
Sotti's solution 很棒!它的工作原理与基础组件的工作原理完全一样。
在我的例子中,选项卡可以根据过滤器的变化动态发展,所以我做了一些小的调整,以允许使用 redraw() 方法更新选项卡模式。它也在 Kotlin 中
class AdaptiveTabLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : TabLayout(context, attrs, defStyleAttr) {
private var gravityAndModeSeUpNeeded = true
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
if (gravityAndModeSeUpNeeded) {
setModeAndGravity()
}
}
fun redraw() {
post {
tabMode = MODE_SCROLLABLE
gravityAndModeSeUpNeeded = true
invalidate()
}
}
private fun setModeAndGravity() {
val tabCount = tabCount
val screenWidth = Utils.getScreenWidth()
val minWidthNeedForMixedMode = getMinSpaceNeededForFixedMode(tabCount)
if (minWidthNeedForMixedMode == 0) {
return
} else if (minWidthNeedForMixedMode < screenWidth) {
tabMode = MODE_FIXED
tabGravity = if (Utils.isBigTablet()) GRAVITY_CENTER else GRAVITY_FILL
} else {
tabMode = MODE_SCROLLABLE
}
gravityAndModeSeUpNeeded = false
}
private fun getMinSpaceNeededForFixedMode(tabCount: Int): Int {
val linearLayout = getChildAt(0) as LinearLayout
var widestTab = 0
var currentWidth: Int
for (i in 0 until tabCount) {
currentWidth = linearLayout.getChildAt(i).width
if (currentWidth == 0) return 0
if (currentWidth > widestTab) {
widestTab = currentWidth
}
}
return widestTab * tabCount
}
init {
tabMode = MODE_SCROLLABLE
}
}
如果你喜欢这个片段,请点赞 Sotti 的解决方案,它为我节省了很多时间。