How to use Segmented Buttons to switch between fragments?

469 Views Asked by At

I need to get the following design.

  • I need a way without using TabLayout/ViewPager.
  • There is no swipe required while switching in between the fragments.

Could you please help to understand how this can be designed with Segmented Buttons?

SegmentedButtons

3

There are 3 best solutions below

1
Muzammal Abdul Ghafoor On

enter image description here In your layout.

<com.google.android.material.button.MaterialButtonToggleGroup
    android:id="@+id/toggleButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <Button
        style="?attr/materialButtonOutlinedStyle"
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1"
    />
    <Button
        style="?attr/materialButtonOutlinedStyle"
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 2"
    />
    <Button
        style="?attr/materialButtonOutlinedStyle"
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 3"
    />
</com.google.android.material.button.MaterialButtonToggleGroup>

In code:

toggleButton.addOnButtonCheckedListener { toggleButton, checkedId, isChecked ->
    // Respond to button selection
}

Reference: https://github.com/material-components/material-components-android/blob/master/docs/components/Button.md#toggle-button

5
MariosP On

To draw the outer/border section you can simply use a MaterialCardView as the parent by using the properties app:strokeWidth and app:strokeColor and add the MaterialButtonToggleGroup as a child of it. The MaterialButtonToggleGroup has the property app:singleSelection="true" to act as a single selection and the property app:selectionRequired="true" to disable the deselection effect.

Example:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:background="@android:color/black">

    <com.google.android.material.card.MaterialCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:cardBackgroundColor="@android:color/black"
        app:cardCornerRadius="0dp"
        app:strokeWidth="1dp"
        app:strokeColor="@android:color/white">

        <com.google.android.material.button.MaterialButtonToggleGroup
            android:id="@+id/toggleButton"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_margin="5dp"
            android:background="@android:color/black"
            app:singleSelection="true"
            app:selectionRequired="true"
            app:checkedButton="@id/button1">

            <com.google.android.material.button.MaterialButton
                style="@style/MyButtonStyle"
                android:id="@+id/button1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:text="All"
                android:insetTop="0dp"
                android:insetLeft="0dp"
                android:insetRight="0dp"
                android:insetBottom="0dp"
                app:cornerRadius="0dp"
                app:rippleColor="@android:color/transparent"/>

            <com.google.android.material.button.MaterialButton
                style="@style/MyButtonStyle"
                android:id="@+id/button2"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:text="Favourites"
                android:insetTop="0dp"
                android:insetLeft="0dp"
                android:insetRight="0dp"
                android:insetBottom="0dp"
                app:cornerRadius="0dp"
                app:rippleColor="@android:color/transparent"/>

        </com.google.android.material.button.MaterialButtonToggleGroup>

    </com.google.android.material.card.MaterialCardView>

</androidx.constraintlayout.widget.ConstraintLayout>

where style="@style/MyButtonStyle" is a custom style for each MaterialButton like the below:

<style name="MyButtonStyle" parent="Widget.MaterialComponents.Button">
    <item name="backgroundTint">@color/button_background_color_selector</item>
    <item name="android:textColor">@color/button_text_color_selector</item>
    <item name="android:textAllCaps">false</item>
</style>

From above the @color/button_background_color_selector is the tab background color selector:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#5F9EA0" android:state_checked="true"/>
    <item android:color="@android:color/transparent" android:state_checked="false"/>
</selector>

and the @color/button_text_color_selector is the tab text color selector:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@android:color/white" android:state_checked="true"/>
    <item android:color="@android:color/darker_gray" android:state_checked="false"/>
</selector>

Result:

all

favourites

For Custom Tab background Image:

In case you have a custom background Image for the MaterialButton you can't use anymore the MaterialButtonToggleGroup because it will throw an error like this:

"Attempted to get ShapeAppearanceModel from a MaterialButton which has an overwritten background"

A simple solution is to replace the current MaterialButtonToggleGroup with a LinearLayout something like below:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:background="@android:color/black">

    <com.google.android.material.card.MaterialCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:cardBackgroundColor="@android:color/black"
        app:cardCornerRadius="0dp"
        app:strokeWidth="1dp"
        app:strokeColor="@android:color/white">

        <LinearLayout
            android:id="@+id/toggleButton"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_margin="5dp"
            android:background="@android:color/black">

            <com.google.android.material.button.MaterialButton
                style="@style/MyButtonStyle"
                android:id="@+id/button1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:text="All"
                android:insetTop="0dp"
                android:insetLeft="0dp"
                android:insetRight="0dp"
                android:insetBottom="0dp"
                app:cornerRadius="0dp"
                app:rippleColor="@android:color/transparent"/>

            <com.google.android.material.button.MaterialButton
                style="@style/MyButtonStyle"
                android:id="@+id/button2"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:text="Favourites"
                android:insetTop="0dp"
                android:insetLeft="0dp"
                android:insetRight="0dp"
                android:insetBottom="0dp"
                app:cornerRadius="0dp"
                app:rippleColor="@android:color/transparent"/>

        </LinearLayout>

    </com.google.android.material.card.MaterialCardView>

</androidx.constraintlayout.widget.ConstraintLayout>

where style="@style/MyButtonStyle" now is the below:

<style name="MyButtonStyle" parent="Widget.MaterialComponents.Button">
    <item name="backgroundTint">@null</item>
    <item name="android:background">@drawable/button_background_drawable_selector</item>
    <item name="android:checkable">true</item>
    <item name="android:textColor">@color/button_text_color_selector</item>
    <item name="android:textAllCaps">false</item>
</style>

From the above you can use the property android:background to add a custom background drawable selector. In our case the @drawable/button_background_drawable_selector is defined under the drawable folder like the below:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/my_drawable" android:state_checked="true"/>
    <item android:drawable="@android:color/transparent" android:state_checked="false"/>
</selector>

And programmatically you have to handle which button is now selected by using the setChecked() method like the below:

MaterialButton button1 = findViewById(R.id.button1);
MaterialButton button2 = findViewById(R.id.button2);

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        button1.setChecked(true);
        button2.setChecked(false);
    }
});
button2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        button2.setChecked(true);
        button1.setChecked(false);
    }
});
0
Nguyễn Quang Thọ On

if material button cannot apply gradient background color, so you can use AppCompatToggleButton:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <androidx.appcompat.widget.AppCompatToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/button_selector"
        android:textOff="App"
        android:checked="true"
        android:textOn="App" />

    <androidx.appcompat.widget.AppCompatToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/button_selector"
        android:checked="false"
        android:textOff="favorite"
        android:textOn="favorite" />
</LinearLayout>

button_selector.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:drawable="@drawable/gradient_button_background"/>
    <item android:drawable="@drawable/default_button_background"/>
</selector>

gradient_button_background.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:angle="90"
        android:endColor="#FFFFFF"
        android:startColor="#00FF00"
        android:type="linear" />
</shape>

default_button_background.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#002196F3" />
</shape>

toogle button with gradient background