Android Support Design TabLayout: Gravity Center and Mode Scrollable

前端 未结 13 1991

I am trying to use the new Design TabLayout in my project. I want the layout to adapt to every screen size and orientation, but it can be seen correctly in one orientation.<

相关标签:
13条回答
  • 2020-12-02 07:28

    I created an AdaptiveTabLayout class to achieve this. This was the only way I found to actually solve the problem, answer the question and avoid/workaround problems that other answers here don't.

    Notes:

    • Handles phone/tablet layouts.
    • Handles cases where there's enough room for MODE_SCROLLABLE but not enough room for MODE_FIXED. If you don't handle this case it's gonna happen on some devices you'll see different text sizes or oven two lines of text in some tabs, which looks bad.
    • It gets real measures and doesn't make any assumptions (like screen is 360dp wide or whatever...). This works with real screen sizes and real tab sizes. This means works well with translations because doesn't assume any tab size, the tabs get measure.
    • Deals with the different passes on the onLayout phase in order to avoid extra work.
    • Layout width needs to be wrap_content on the xml. Don't set any mode or gravity on the 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;
        }
    
    }
    

    And this is the DeviceUtils class:

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

    Use example:

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

    Problems/Ask for help:

    • You'll see this:

    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
    

    I'm not sure how to solve it. Any suggestions?

    • To make the TabLayout child measures, I'm making some castings and assumptions (Like the child is a LinearLayout containing other views....) This might cause problems with in further Design Support Library updates. A better approach/suggestions?
    0 讨论(0)
提交回复
热议问题