Disable TabLayout
Asked Answered
C

13

58

I'm using the new class provided by the design library : TabLayout. And I want in specific cases that the one I'm using can't change tab anymore.

I manage to disable swipe on its viewpager but I can't figure out how to disable the change of page by clicking on tabs.

Thank's in advance.

Carr answered 29/7, 2015 at 14:0 Comment(6)
How did you disable swipe?Dreadnought
You need to disable swipe on his ViewPager. I send you to this post : #9650765Carr
@Shargotth How did you disable swipe??Cacka
Look at the comment above. If you don't understand the solution in the link I could try to explain it with other words.Carr
int tabPositionToDisable=1; LinearLayout tabStrip = ((LinearLayout)tablayout.getChildAt(0)); tabStrip.getChildAt(tabPositionToDisable).setClickable(false); tabStrip.getChildAt(tabPositionToDisable).setEnabled(false);Ishtar
@pravin I think no need of tabStrip.getChildAt(tabPositionToDisable).setClickable(false); only setEnabled() worked for me.Ferricyanide
B
108

I had the same problem and I solved it disabling touch event on tabs with the following code:

  LinearLayout tabStrip = ((LinearLayout)mTabLayout.getChildAt(0));
    for(int i = 0; i < tabStrip.getChildCount(); i++) {
        tabStrip.getChildAt(i).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return true;
            }
        });
    }
Biskra answered 19/8, 2015 at 20:24 Comment(3)
I'm calling these lines in onCreateView but not working :/Pegboard
This worked very well but is it safe? Is there any situation where it may fail?Sandasandakan
@Sandasandakan this could fail in the future as Google deprecates more uses of reflection like this with each major release of Android. Other than that it has proven safe for me in high use production applications for many years.Schizothymia
S
44

I found a similar answer that is a little more simple and also allows you to re-enable the tabs later if you wanted to without having to deal with overriding the onTouch event.

TabLayout tabLayout = (TabLayout)  mParentView.findViewById(R.id.my_tabs);

LinearLayout tabStrip = ((LinearLayout)tabLayout.getChildAt(0));
tabStrip.setEnabled(false);
for(int i = 0; i < tabStrip.getChildCount(); i++) {
    tabStrip.getChildAt(i).setClickable(false);
}

And if you want to re-enable the tabs just set tabStrip.setEnabled and setClickable for the child elements to true

LinearLayout tabStrip = ((LinearLayout)tabLayout.getChildAt(0));
tabStrip.setEnabled(true);
for(int i = 0; i < tabStrip.getChildCount(); i++) {
    tabStrip.getChildAt(i).setClickable(true);
}
Schizothymia answered 1/2, 2016 at 4:34 Comment(4)
@user1269737 could you be more specific I'd like to fix this, what do you mean by indicatorSchizothymia
There's Text and Indicator(line below the Text). This indicator is still clickable.Forked
@user1269737 thanks for pointing this out I will update my answer as I use this often enoughSchizothymia
This answer just disable touch but by scrolling user can go to disable tab so it's not good.Velamen
C
22

Very similar to the answer by pat8719 but just disabling the tabs is sufficient to prevent them from being selected.

TabLayout tabLayout = (TabLayout)  mParentView.findViewById(R.id.my_tabs);
TabLayoutUtils.enableTabs( tabLayout, false );

TabLayoutUtils class

public class TabLayoutUtils {

    public static void enableTabs(TabLayout tabLayout, Boolean enable){
        ViewGroup viewGroup = getTabViewGroup(tabLayout);
        if (viewGroup != null)
            for (int childIndex = 0; childIndex < viewGroup.getChildCount(); childIndex++)
            {
                View tabView = viewGroup.getChildAt(childIndex);
                if ( tabView != null)
                    tabView.setEnabled(enable);
            }
    }

    public static View getTabView(TabLayout tabLayout, int position){
        View tabView = null;
        ViewGroup viewGroup = getTabViewGroup(tabLayout);
        if (viewGroup != null && viewGroup.getChildCount() > position)
            tabView = viewGroup.getChildAt(position);

        return tabView;
    }

    private static ViewGroup getTabViewGroup(TabLayout tabLayout){
        ViewGroup viewGroup = null;

        if (tabLayout != null && tabLayout.getChildCount() > 0 ) {
            View view = tabLayout.getChildAt(0);
            if (view != null && view instanceof ViewGroup)
                viewGroup = (ViewGroup) view;
        }
        return viewGroup;
    }

}
Cyclohexane answered 13/5, 2016 at 2:45 Comment(0)
C
20

For me the working approach is this:

bool condition = ...
foreach (var view in _tabLayout.Touchables)
    view.Clickable = condition;

This is 100% safe, as getTouchables() call is supported since API 1. No need to manually traverse layout or something. I consider it to be much simpler than that of the accepted answer, but only when all the tabs have to be marked not clickable.

P.S.: example is on C#, as I am working with Xamarin, but it is fairly easy to translate it back to Java. Enjoy! =)

Kotlin example:

tabLayout.touchables.forEach { it.isClickable = false }

Java example:

for (View v: tabLayout.getTouchables())
    v.setClickable(false);
Ciborium answered 22/2, 2018 at 15:42 Comment(5)
This is what I did. I also lowered the alpha on the view to have a visual indicator of the disabled/enabled state of the tab.Credo
It's worth noting that this is good for disabling, but fails to re-enable. That's because once you set a view as not-clickable, it will no longer get returned by getTouchables(). I supposed you could store the views after your first call or something, but that gets dangerous if you ever expect the hierarchy to change.Brandebrandea
I want to toggle it. I'm unable to turn clickable on.Dzerzhinsk
Worked Perfectly for disabling the clickable effect, which was just what I needed.... But doesn't work if you wanna toggle it back onBushwhack
If you are going to use ViewPager2 this solution will work in both ways. Disable and enable. https://mcmap.net/q/331380/-how-can-i-disable-click-on-tablayout-in-androidFlair
S
15

A good trick you can use :

create a frame layout that cover the view(s) you want to protect from a click like the following :

<FrameLayout
    android:clickable="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

This code will create and empty/transparent view on top of your view. The android:clickable="true" will intercept the click and prevent the click to go through the view !

This hack can probably be optimized but its few lines of code to protect multiple view at the same time !

Standstill answered 6/6, 2016 at 13:20 Comment(2)
This may be a "hack", but IMO it's a much better approach than the other answers, relying on the specifics of TabLayout's internal implementation. I needed to create an "intro" overlay over the entire app and disable everything under it while it's running, so this is a simple yet effective magic bullet.Yester
Best solution everCancel
C
5

If you are using a custom view for the Tab, you can use View#getParent() to get a reference to the Tab's View if you don't want to look through the ViewGroups.

Note: Using the custom view itself instead of the parent may not work because it can have margin, allowing the user to click in the empty space and change the tab still.

View tabView = (View) tab.getCustomView().getParent();

tabView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return true;
    }
});

//or

tabView.setEnabled(false);

The OnTouchListener way and the setEnabled() way do different things, but have the same effect. I prefer the one-liner.

Again, this only works if you use a custom view, otherwise the getParent() call will cause a NullPointerException.

Cassondracassoulet answered 16/6, 2017 at 0:16 Comment(0)
C
4

It's also possible to avoid child clicks by extending TabLayout class and intercepting all the touch events.

class NonTouchableTabLayout : TabLayout {

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
        return true
    }
}

If you want to enable clicks again just return false on onInterceptTouchEvent method.

Cobham answered 30/4, 2019 at 11:38 Comment(1)
This is the cleanest solution imo. Other answers that rely on the internal structure of TabLayout concern me as being potentially quite fragile. I changed it a bit and implemented override fun onInterceptTouchEvent(event: MotionEvent): Boolean { return !isEnabled } So then when I set tabLayout.isEnabled = false it stops click eventsBomber
A
1

Based on Michele's Accepted Answer, to disable click for a single tab in Kotlin:

Inside the tabs-containing activity:

val tabs: TabLayout = findViewById(R.id.tabs)
...

(tabs.getChildAt(0) as LinearLayout).getChildAt(desiredTabPositionHere).isClickable = false

Inside the fragments (tabs):

(requireActivity().tabs.getChildAt(0) as LinearLayout).getChildAt(desiredTabPositionHere).isClickable = false

You can turn the click function on and off (by setting true or false). With the same method, it is also possible to modify visibility, alpha, backgroundColor etc.

Abie answered 8/3, 2020 at 0:49 Comment(0)
W
1

In my use case, if the user is in a tab and presses one of the buttons, a calculation is launched and I don't want the user to be able to switch tabs until after the calculation is completed. So I used the answer by Vladislav Rishe but modified a little as follows...

In JAVA:

//'tabLayout' is the variable where you reference your tab layout...
//A container to save the tabs that were disabled
private ArrayList<View> touchablesToRestore = new ArrayList<View>();

public void enableTabs() {
    for(View v: touchablesToRestore){
        v.setClickable(true);
    }
    //After you enable them all, clear the container
    touchablesToRestore.clear();
}

public void disableTabs(){
    for(View v: tabLayout.getTouchables()){
        //Add the tab that is being disabled to the container
        touchablesToRestore.add(v);
        v.setClickable(false);
    }
}

So when the user taps the button to make a calculation, just before the calculation logic is called, I call disableTabs(), then once the calculation is completed and it has been displayed, I call enableTabs()...

Wb answered 17/10, 2020 at 2:11 Comment(0)
S
0

check my answer here Disable Tabs in TabLayout

You can for loop tab and use this function to disable all tab

Skirt answered 19/12, 2018 at 13:48 Comment(0)
H
0

Based on this excellent solution , if someone needs it in Kotlin :

  val tabStrip = mTabLayout.getChildAt(0) as LinearLayout
    for (i in 0..tabStrip.childCount) {
        tabStrip.getChildAt(i).setOnTouchListener(object : View.OnTouchListener{
            override fun onTouch(p0: View?, p1: MotionEvent?): Boolean {
                return true
            }
        })
    }

Or a more elegant solution with lambdas

mTabLayout.setOnTouchListener {v: View, m: MotionEvent ->
        true
 }
Hexone answered 15/5, 2019 at 13:2 Comment(1)
There is a mistake in your kotlin code. It should be i in 0 until tabStrip.childCount. Using .. you expect an inclusive range which is not what you want here. It will crash.Chukar
K
0

We can disable & re-enable all tabs as below:

Kotlin

Disable all tabs

fun disableAllTabs() {
    (binding.tlCommentTabs.getChildAt(0) as? ViewGroup)?.forEach { it.isEnabled = false }
    (binding.tlCommentTabs.getChildAt(1) as? ViewGroup)?.forEach { it.isEnabled = false }
}

Enable all tabs

fun enableAllTabs() {
    (binding.tlCommentTabs.getChildAt(0) as? ViewGroup)?.forEach { it.isEnabled = true }
    (binding.tlCommentTabs.getChildAt(1) as? ViewGroup)?.forEach { it.isEnabled = true }
}

Where, binding.tlCommentTabs is an instance of TabLayout. I am using view binding. There is no need to add an extra wrapper view in the hierarchy.

Kif answered 10/1, 2022 at 5:35 Comment(0)
S
0

Comprehensive solution for both ViewPager to prevent swiping between tabs, and TabLayout to prevent clicking the tabs when they are disabled.

First created the CustomViewPager and CustomTabLayout classes (inheriting the fuctionality of ViewPager and TabLayout base classes)

// content of CustomViewPager.java
package mypackage;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;

import androidx.viewpager.widget.ViewPager;

public class CustomViewPager extends ViewPager {

    private boolean enableSwipe;

    public CustomViewPager(Context context) {
        super(context);
        init();
    }

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

    private void init() {
        enableSwipe = true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return enableSwipe && super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return enableSwipe && super.onTouchEvent(event);
    }

    public void setEnableSwipe(boolean enableSwipe) {
        this.enableSwipe = enableSwipe; 
    }
}

// content of CustomTabLayout.java
package mypackage;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;

import com.google.android.material.tabs.TabLayout;

public class CustomTabLayout extends TabLayout {
    private boolean enableTabs;

    public CustomTabLayout(Context context) {
        super(context);
        init();
    }

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

    private void init() {
        enableTabs = true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return !enableTabs;
    }

    public void setEnableTabs(boolean enable) {
        this.enableTabs = enable;
    }
}

create the UI:

        <mypackage.CustomTabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary" />

        <mypackage.CustomViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="100dp"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

in the onCreate create the relation between the two controls:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CustomViewPager viewPager = findViewById(R.id.view_pager);
        viewPager.setAdapter(sectionsPagerAdapter);
        CustomTabLayout tabs = findViewById(R.id.tabs);
        tabs.setupWithViewPager(viewPager);
    }

EnableControls function to enable/disable the tab:

private void EnableControls(boolean b) {
        CustomViewPager viewPager = findViewById(R.id.view_pager);
        CustomTabLayout tabs = findViewById(R.id.tabs);

        viewPager.setEnableSwipe(b);
        tabs.setEnableTabs(b);
}
Superfamily answered 13/2, 2022 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.