AndroidサポートデザインTabLayout:重力センターとモードスクロール可能


98

プロジェクトで新しいDesign TabLayoutを使用しようとしています。レイアウトをすべての画面サイズと向きに適合させたいのですが、1つの向きで正しく表示できます。

私は重力とモードを扱い、私のtabLayoutを次のように設定しています:

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

そのため、スペースがない場合はtabLayoutはスクロール可能ですが、スペースがある場合は中央に配置されます。

ガイドから:

public static final int GRAVITY_CENTER TabLayoutの中央にタブをレイアウトするために使用される重力。

public static final int GRAVITY_FILL重力は、TabLayoutを可能な限り埋めるために使用されます。このオプションは、MODE_FIXEDと共に使用した場合にのみ有効になります。

public static final int MODE_FIXED固定タブはすべてのタブを同時に表示し、タブ間の迅速なピボットの恩恵を受けるコンテンツで使用するのに最適です。タブの最大数は、ビューの幅によって制限されます。固定タブの幅は、最も広いタブラベルに基づいて同じです。

public static final int MODE_SCROLLABLEスクロール可能なタブは、いつでもタブのサブセットを表示し、より長いタブラベルと多数のタブを含めることができます。これらは、ユーザーがタブラベルを直接比較する必要がないタッチインターフェイスのコンテキストを閲覧するのに最適です。

したがって、GRAVITY_FILLはMODE_FIXEDとのみ互換性がありますが、atはGRAVITY_CENTERに対して何も指定していません。MODE_SCROLLABLEと互換性があると思いますが、これはGRAVITY_CENTERおよびMODE_SCROLLABLEを使用して取得するものです。

ここに画像の説明を入力してください

したがって、両方の方向でSCROLLABLEを使用していますが、GRAVITY_CENTERは使用していません。

これは私が風景に期待することです。しかし、これを行うには、MODE_FIXEDを設定する必要があるため、ポートレートで取得できるのは次のとおりです。

ここに画像の説明を入力してください

tabLayoutが画面に収まる場合、GRAVITY_CENTERがSCROLLABLEに対して機能しないのはなぜですか?重力とモードを動的に設定する(そして私が期待していることを確認する)方法はありますか?

どうもありがとうございました!

編集:これは私の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" />

@ Fighter42これに対する解決策を見つけましたか?私も同じ問題に直面しています。
Spynet、2015

私が見つけた唯一の方法は、タブのサイズを(手動で)取得し、それが適切かどうかに応じてレイアウトパラメータを動的に構成することでした。
ハビエルデルガード

@ Fighter42、使用しているソリューションのサンプルコードを投稿できますか?
サミーズ2015

1
私の答えは論理的には良くありませんが、それは私に役立ちます。テキストの前後にスペースを入れます。その後、スクロール可能になります。両方のモードで。
AmmY 2015

回答:


127

タブ重力のみが影響しMODE_FIXEDます。

可能な解決策の1つは、layout_widthwrap_contentlayout_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
アプリにマテリアルデザインを使用しています。私はandroidhive.info/2015/09/からのサンプルに従いましたスクロール可能なタブは、キットカットおよびジェリービーンのデバイスでは機能しません。ロリポップではタブをスクロールできますが、ロリポップ以外のデバイスではタブスクロールは機能しませんが、スワイプは正常に機能します。何が問題になるかわかりますか。
user2681579

これは、API 15 / support.design:25.1.0では機能しませんでした。ウィンドウの幅よりも狭い場合、タブは左揃えされました。タブの設定 app:tabMaxWidth="0dp"android:layout_centerHorizontal="true"中央揃えに取り組みました。
ベイカー

1
それは私のために働いた。タブは常に中央に配置されますが、実際にスクロールできない場合、幅全体に表示されません。
ホセ・Augustinho

14

これは私がやった方法です

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のソリューションに小さな変更を加えました


このソリューションには、ティアゴのソリューションで述べたのと同じ問題があります。
ソッティ2016年

このソリューションはアイコンなしのタブで機能しますが、テキスト付きのアイコンがある場合はどうなりますか?:/
Emre Tekin

@EmreTekinはい、アイコンで機能します。タブにはテキスト付きのアイコンがありました
Flux

tabLayout.getWidth()私にとっては常にゼロを返します
ishandutta2007

9

物事をシンプルに保つには、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" />

ここに画像の説明を入力してください


7
これは作成者が求めているものではありません。このアプローチの問題は、テキストが小さい電話に2つのタブまたは3つの3つのタブがあるシナリオでは、タブが左側にあるGoogle Playストアのようなレイアウトになることです。作成者は、可能な場合はタブを端から端まで到達させ、それ以外の場合はスクロールさせたいと考えています。
ソッティ2016年

同じ問題。あなたはそれを修正できますか?
Hoang Duc Tuan

8

この動作が発生する理由が見つからなかったので、次のコードを使用しました。

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

基本的に、tabLayoutの幅を手動で計算する必要があります。次に、tabLayoutがデバイスに収まるかどうかに応じて、タブモードを設定します。

レイアウトのサイズを手動で取得する理由は、スクロール可能モードではすべてのタブの幅が同じではないためです。この例では、一部の名前で2行が使用されている可能性があります。


このソリューションを使用すると、テキストが縮小され、2行も使用される可能性があります。それに関するこのスレッドの他の回答と解決策を参照してください。これが表示されるリスクを軽減しますが、いずれにしても、TabLayoutがscreenWidthよりも少し小さいときに頻繁に表示されます。
ソッティ2016年

7

android-tablayouthelperを見てください

TabLayout.MODE_FIXEDを自動的に切り替え、TabLayout.MODE_SCROLLABLEは合計タブ幅に依存します。


ただし、タブタイトルが動的な場合。テキストは次の行に入ります。タブのカスタムレイアウトを使用する場合。次のようにテキストの楕円を最後に
AndroidGeek

4

これは、SCROLLABLEFIXED+ を自動的に切り替えるために使用したソリューション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ソリューションを使用することをお勧めします。


これはうまく機能します。ランドスケープとポートレートを切り替えると、期待どおりの結果が得られます。Googleはこれをタブ付きアクティビティのデフォルトテンプレートに実装する必要があります。
誰かどこか

この答えには、ティアゴのソリューションで述べたのと同じ問題があります。
Sotti、2016年

4

これを実現するために、AdaptiveTabLayoutクラスを作成しました。これは、実際に問題を解決し、質問に答え、他の答えでは解決できない問題を回避/回避するために私が見つけた唯一の方法でした。

ノート:

  • 携帯電話/タブレットのレイアウトを処理します。
  • MODE_SCROLLABLEは十分なスペースがあるが、十分なスペースがないケースを処理しMODE_FIXEDます。このケースを処理しないと、一部のデバイスで異なるテキストサイズが表示されたり、一部のタブに2行のテキストが表示されたりして、見栄えが悪くなります。
  • 実際の測定値を取得し、想定を行いません(画面の幅が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>

要旨

問題/助けを求める:

  • これが表示されます:

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のように...)。これにより、デザインサポートライブラリの更新で問題が発生する可能性があります。より良いアプローチ/提案?

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

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、2016年

1
別の問題があります。このアプローチは、TabLayoutの合計幅を測定して、スクロール可能かどうかを決定します。それはあまり正確ではありません。スクロール可能なモードでTabLayoutが両端に達しない状況がありますが、タブの行数を乱用するというテキスト(通常は2)を縮小しないと、固定モードに十分なスペースがありません。これは、固定モードのタブの幅(screenWidth / numberOfTabs)が固定されている直接の結果ですが、スクロール可能モードでは、タブはテキスト+パディングを折り返します。
Sotti、2016年

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

これは作成者が求めているものではありません。ここでの問題は、タブがスクロールできないことであり、画面のスペースがなくなります。タブはテキストを縮小し始め、最終的には2つの罫線が表示されます。
Sotti、2016年

1

私は以下を使用してこれを解決しました

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

    }

1

非常に単純な例であり、常に機能します。

/**
 * 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);

    });

}

アイデアをありがとう。私の場合は少し変更して、魅力のように動作します
ErenTüfekçiJan

1

私の最終的な解決策

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
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.