How to add a switch to android action bar?

Solution 1:

Create a layout for the switch switch_layout.xml. Custom layouts for menu should always be RelativeLayout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <Switch
        android:id="@+id/switchForActionBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />

</RelativeLayout>

Then, in your mainmenu.xml add the item as follows

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/myswitch"
        android:title=""
        android:showAsAction="always"
        android:actionLayout="@layout/switch_layout"
    />   
</menu>

And in your activity, inflate the mainmenu.xml as you always do

getMenuInflater().inflate(R.menu.mainmenu, menu);
return true;

Solution 2:

Finally figured out my problem: for those that's using the new AppCompat, you should be using android.support.v7.widget.SwitchCompat instead of Switch on the switch layout...otherwise, it won't show on the ActionBar (assumed you're using AppCompat ActionBar as well), well, the actionLayout attribute doesn't work, it has to be set in the code.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/switchView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <android.support.v7.widget.SwitchCompat
        android:id="@+id/switchForActionBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="" />

</RelativeLayout>

Then set the layout in the code:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    MenuItem item = menu.findItem(R.id.on_off_switch);
    item.setActionView(R.layout.on_off_switch);
    return true;
}

Solution 3:

If the widget is not appearing in the action-bar it is probably because you are using appCompat for your action-bar. To solve this switch "android:" to "app:" in front of "showAsAction" and "actionLayout" in your menu.xml

Add item to xml, with app: in place of android:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
            <item
                android:id="@+id/myswitch"
                android:title=""
                app:showAsAction="always"
                app:actionLayout="@layout/switch_layout"
            />   
        </menu>

Make layout that you are using for your "app:actionLayout"
switch_layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <Switch
        android:id="@+id/switchAB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        />
</RelativeLayout>

Inflate the menu in your ActionBarActivity as you would normally

   getMenuInflater().inflate(R.menu.mainmenu, menu);
    return true;

This should make the switch appear in your action-bar, if it was not appearing.

Solution 4:

For those who want to add,

Checked change Listener to the same Switch

Kotlin

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.menu_main, menu)
    val item = menu!!.findItem(R.id.my_switch_item)
    item.setActionView(R.layout.switch_layout)

    val mySwitch = item.actionView.findViewById(R.id.switch_id)
    mySwitch.setOnCheckedChangeListener(object : CompoundButton.OnCheckedChangeListener{
        override fun onCheckedChanged(p0: CompoundButton?, isChecked: Boolean) {
            // do what you want with isChecked
        }
    })

    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    MenuItem item = menu.findItem(R.id.my_switch_item);
    item.setActionView(R.layout.switch_layout);

    Switch mySwitch = item.getActionView().findViewById(R.id.switch_id);
    mySwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                  // do something based on isChecked
         }
    });
    return true;
}

P.S. You can change reference to Switch or SwitchCompat

Solution 5:

The solution given by Ezequiel is awesome and works. Here goes another approach:

Define your custom layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" >
    <Switch
        android:id="@+id/actionbar_switch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />
</RelativeLayout>

Inflate it programatically:

ActionBar actionBar = getSupportActionBar();
actionBar.setCustomView(R.layout.actionbar_top);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_CUSTOM);
...
Switch button = (Switch) findViewById(R.id.actionbar_switch);