【问题标题】:Tabs don't fit to screen with tabmode=scrollable, Even with a Custom Tab Layout选项卡不适合使用 tabmode=scrollable 的屏幕,即使使用自定义选项卡布局
【发布时间】:2016-12-24 11:14:56
【问题描述】:

我已经使用ViewPager 制作了一个自定义 TabLayout,并在可滚动模式下使用 TabLayout:

我需要它是可滚动的,因为日期的数量可以多达 15-20 个:

<com.example.project.recommendedapp.CustomScrollableTabLayout
    android:id="@+id/tab_layout_movie"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimary"
    android:elevation="6dp"
    android:minHeight="?attr/actionBarSize"
    app:tabGravity="fill"
    app:tabMode="scrollable"
    app:tabTextAppearance="?android:textAppearanceMedium"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

我根据另一个类似问题使用的自定义类: TabLayout not filling width when tabMode set to 'scrollable'

自定义标签布局类是:

package com.example.project.recommendedapp;

import android.content.Context;
import android.graphics.Point;
import android.support.design.widget.TabLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;

import java.lang.reflect.Field;

public class CustomScrollableTabLayout extends TabLayout {
    private Context mContext;

    Point size;

    public CustomScrollableTabLayout(Context context) {
        super(context);
        mContext=context;
        size = new Point();
    }

    public CustomScrollableTabLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext=context;
        size = new Point();
    }

    public CustomScrollableTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext=context;
        size = new Point();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        try {
            if (getTabCount() == 0) {
                return;
            }else {
                Display display = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
                display.getSize(size);
                int width = size.x;

                Field field = TabLayout.class.getDeclaredField("mScrollableTabMinWidth");
                field.setAccessible(true);
                field.set(this, (width / getTabCount()));

                Log.d("FragmentCreate",String.valueOf(width / getTabCount()));
            }
        } catch (Exception e) {
            Log.e("FragmentCreate","Error while setting width",e);
        }
    }
}

【问题讨论】:

  • 不幸的是,这似乎是 Google 想要的样子。
  • 您可以尝试将您的 TabLayout 放在布局中并将重力设置为 center_horizo​​ntal。

标签: android android-tablayout


【解决方案1】:

这是我的解决方案。

public class MyTabLayout extends TabLayout {    
    public MyTabLayout(Context context) {
        super(context);
    }

    public MyTabLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        ViewGroup tabLayout = (ViewGroup)getChildAt(0);

        int childCount = tabLayout.getChildCount();

        int widths[] = new int[childCount+1];

        for(int i = 0; i < childCount; i++){
            widths[i] = tabLayout.getChildAt(i).getMeasuredWidth();
            widths[childCount] += widths[i];
        }

        int measuredWidth = getMeasuredWidth();
        for(int i = 0; i < childCount; i++){
            tabLayout.getChildAt(i).setMinimumWidth(measuredWidth*widths[i]/widths[childCount]);
        }

    }

}

【讨论】:

    【解决方案2】:

    我的解决方案略有不同,因为我尝试了上述解决方案,但它在某些设备上不起作用。我注意到如果所有选项卡都在屏幕上可见并且如果我们将tabMode 设置为fixedTabLayout 会填充其给定宽度。当我们使用scrollable 时,TabLayout 的行为就像它的tabGravity 设置为center

    所以,我计算TabLayout 中所有选项卡的宽度总和,如果小于测量宽度,我将其tabMode 设置为MODE_FIXED,否则设置为MODE_SCROLLABLE

    public class CustomTabLayout extends TabLayout {
    
        private static final String TAG = CustomTabLayout.class.getSimpleName();
    
        public CustomTabLayout(Context context) {
            super(context);
        }
    
        public CustomTabLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public CustomTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (getTabCount() == 0)
                return;
            try {
                ViewGroup tabLayout = (ViewGroup)getChildAt(0);
                int widthOfAllTabs = 0;
                for (int i = 0; i < tabLayout.getChildCount(); i++) {
                    widthOfAllTabs += tabLayout.getChildAt(i).getMeasuredWidth();
                }
                setTabMode(widthOfAllTabs <= getMeasuredWidth() ? MODE_FIXED : MODE_SCROLLABLE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      我到处寻找这个确切问题的答案。在我的情况下,我是动态添加和删除选项卡,所以我希望它在只有几个选项卡时填满屏幕,但在有太多选项卡时开始滚动,而不是缩小它们或将标题放在两行。使用以下自定义选项卡布局终于让它对我有用。在调用 super.onMeasure()之前设置最小宽度是关键。

      public class CustomTabLayout extends TabLayout {
      
          public CustomTabLayout(Context context) {
              super(context);
          }
      
          public CustomTabLayout(Context context, AttributeSet attrs) {
              super(context, attrs);
          }
      
          public CustomTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
              super(context, attrs, defStyleAttr);
          }
      
          @Override
          protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      
              ViewGroup tabLayout = (ViewGroup)getChildAt(0);
              int childCount = tabLayout.getChildCount();
      
              if( childCount != 0 ) { 
                  DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
                  int tabMinWidth = displayMetrics.widthPixels/childCount;
      
                  for(int i = 0; i < childCount; ++i){
                      tabLayout.getChildAt(i).setMinimumWidth(tabMinWidth);
                  }
              }
      
              super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          }
      }
      

      在 xml 中将标签模式设置为可滚动。

          <com.package.name.CustomTabLayout
              android:id="@+id/my_tabs"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              app:tabMode="scrollable">
      

      【讨论】:

      • 花了2天,终于工作了。这个自定义选项卡布局完美运行。
      • 是的。 onMeasure 成功了。我只是用if(childCount != 0) { DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); int tabMinWidth = displayMetrics.widthPixels/childCount; for(int i = 0; i &lt; childCount; ++i){ tabLayout.getChildAt(i).setMinimumWidth(tabMinWidth); } } 包装它,因为它会使我的应用程序崩溃
      • 这行得通。请务必设置 app:tabPaddingEnd="0dp"app:tabPaddingStart="0dp" 否则您确实会在标签栏上看到一些来自未计入像素的轻微时髦的滚动行为
      【解决方案4】:

      对于那些像我一样从谷歌登陆的人来说,这是我的 2021 Kotlin 解决方案。 它实现了我从其他答案中无法做到的以下几点:

      • 如果选项卡不多,它们会展开以填满整个宽度。
      • 如果有很多选项卡,它们是可滚动的,因此它们不会被压扁。
      • 即使 tabLayout 不是屏幕的全宽也能正常工作。

      为了实现这一点,我创建了一个 TabLayout 的子类,它覆盖了 onMeasure

      class ScalableTabLayout : 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 onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
              val tabLayout = getChildAt(0) as ViewGroup
              val childCount = tabLayout.childCount
              if (childCount != 0) {
                  val widthPixels = MeasureSpec.getSize(widthMeasureSpec)
                  val tabMinWidth = widthPixels / childCount
                  var remainderPixels = widthPixels % childCount
                  tabLayout.forEachChild {
                      if (remainderPixels > 0) {
                          it.minimumWidth = tabMinWidth + 1
                          remainderPixels--
                      } else {
                          it.minimumWidth = tabMinWidth
                      }
                  }
              }
              super.onMeasure(widthMeasureSpec, heightMeasureSpec)
          }
      }
      

      然后在我的布局文件中使用它:

      <com.package.name.ScalableTabLayout
          android:id="@+id/tabs"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          app:tabMaxWidth="0dp"
          app:tabMode="scrollable" />
      

      tabMaxWidth="0dp"tabMode="scrollable" 都是必需的

      【讨论】:

        猜你喜欢
        • 2023-03-09
        • 1970-01-01
        • 2015-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-19
        相关资源
        最近更新 更多