55

I would like to add a button switch similar to jellybean native look. (Blue/gray switch at the top of the view) enter image description here

Documentation shows how to create a menu there or add icons, but it does not say, how to add a custom elements. eg. a switch. http://developer.android.com/guide/topics/ui/actionbar.html

Arturs Vancans
  • 4,531
  • 14
  • 47
  • 76

5 Answers5

100

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;
Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
Ezequiel
  • 1,186
  • 2
  • 11
  • 11
  • 2
    Hey for " Custom layouts for menu should always be RelativeLayout ".Is there any doc state that? – Yeung Sep 25 '13 at 05:48
  • Well, it is true. If I change to FrameLayout, the switch is not easy to touch. – Yeung Sep 25 '13 at 07:40
  • If I use LinearLayout everything works fine here. Thanks for answer! – Erwin Jan 10 '14 at 11:11
  • 12
    you should add `android:layout_centerVertical="true"` to the `Switch` for it to have the same height with the title – Hải Phong Sep 26 '14 at 07:32
  • 1
    Do not forget to add "setHasOptionsMenu(true);" for the fragment to force call onCreateOptionsMenu. – ckp Nov 11 '14 at 15:50
  • HELP! I can't get the switch widget to show... if I set title="On/Off" on the menu item xml, it gets shown (the title) but seem like the layout reference doesn't work, any tips? – Vu Nguyen Dec 05 '14 at 18:26
  • 42
    Use `app` namespace for `actionLayout` and `showAsAction` if switch is not visible – Harsh Vakharia Jan 28 '15 at 11:05
  • For those of you who can't have the switch showing up, changing the namespace to app worked just as Neil pointed out below in one of the answers. +1 – ChallengeAccepted Feb 16 '15 at 09:57
  • 3
    I'm using appcompat, and I used app namespace for actionLayout and showAsAction, but I'm not able to handle its click on `onOptionsItemSelected` method? how to do it? – Firas Al Mannaa May 10 '15 at 15:07
  • Firas Al Manna @ this link may help you or someone http://stackoverflow.com/questions/32091709/how-to-get-action-event-from-actionbar-switch – Amir Aug 19 '15 at 11:31
  • how to add listener on that switch I am trying to add listener by using switch id it is giving null pointer exception – VishAl Oct 18 '16 at 12:11
36

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;
}
Mahesh Babariya
  • 4,560
  • 6
  • 39
  • 54
Vu Nguyen
  • 1,078
  • 9
  • 11
  • 1
    Using `SwitchCompat` has another advantage: the switch is displayed with a more recent look (e.g. material design). – DenisGL Aug 20 '15 at 13:18
  • 15
    Why is everyone using `android:orientation="horizontal"` on RelativeLayout? – zackygaurav Nov 28 '15 at 11:48
  • 1
    @zackygaurav As my test, it does not need to use `RelativeLayout`, I am using `LinearLayout` and it works perfectly. – drakeet Sep 16 '17 at 09:45
33

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.

Neil M.
  • 689
  • 1
  • 8
  • 17
  • This is a subtle but excellent simplification/addition to the above answers! – swooby Feb 08 '15 at 23:03
  • 1
    Its minor details like this that are so easy to oversee and forget to adjust. I always spent hours trying to figure out was wrong. Now if something doesn't show up in the menu, I have a rule of thumb to check the namespaces. – ChallengeAccepted Feb 16 '15 at 10:02
  • I know it has been long but this answer worked for me, now question is how do i get the switch so that i can attach a listener to it programmatically? – clifford_owino Jan 14 '17 at 19:59
13

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

gprathour
  • 14,813
  • 5
  • 66
  • 90
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);
Rudolf Real
  • 1,948
  • 23
  • 27
  • 1
    Wow... this solved my problem!!! I can't seem to work with Ezequiel's solution but this is the solution for me! Maybe it has to do with the new Toolbar I'm using as the ActionBar??? Anyway, thank you! NOTE: that this seems to be different than my original approach where I wanted it to be as a menu's custom view... but is there a way to have this custom view aligned to the right of the action bar? – Vu Nguyen Dec 05 '14 at 19:44
  • Figured out and shared the answer for those who are in the same boat. – Vu Nguyen Dec 05 '14 at 20:12
  • @VuNguyen great! I'm still dealing with it because I have this ActionBar inside a Fragment, findViewById returns null, will update when solved :D – Rudolf Real Dec 05 '14 at 20:18
  • @FabricioPH, I used your approach and it worked. The problem is that the action bar disappeared. What should I do to keep it? – arthursfreire Jan 28 '15 at 19:26
  • 1
    @arthursfreire Hi. I'm not enterely sure what your code looks like, but give it a peak to the method onCreate, specifically after line 80 in here https://github.com/fabriph/peer-instruction/blob/master/student-app-studio/PIStudent/app/src/main/java/com/fabricioph/pistudent/HomeActivity.java – Rudolf Real Jan 28 '15 at 19:57
  • 1
    @arthursfreire thats code full functional myself had written and I'm still working on that project, supporting SDK 16 to 21. – Rudolf Real Jan 28 '15 at 19:59
  • I'm sorry, @FabricioPH, I wrote it wrong. I forgot to type "title" :p The ActionBar did not disappear, its title did it. – arthursfreire Jan 29 '15 at 14:03
  • @FabricioPH, and I tried your approach removing the options to enable the home button and to display home as up, but still didn't work... – arthursfreire Jan 29 '15 at 14:06
  • @arthursfreire in line 100 to 102 I'm setting the option to show the title of the action bar, same file: https://github.com/fabriph/peer-instruction/blob/master/student-app-studio/PIStudent/app/src/main/java/com/fabricioph/pistudent/HomeActivity.java#L101 try that and let me know. – Rudolf Real Jan 29 '15 at 14:42
  • @FabricioPH, I did exactly the same thing as you did. I also tried to change the order of the options, but didn't work out. – arthursfreire Jan 29 '15 at 14:56
  • @arthursfreire how about checking manifest and style.xml, in my manifest check line 24 https://github.com/fabriph/peer-instruction/blob/master/student-app-studio/PIStudent/app/src/main/AndroidManifest.xml#L24 and see the file styles.xml: https://github.com/fabriph/peer-instruction/blob/master/student-app-studio/PIStudent/app/src/main/res/values/styles.xml#L4 – Rudolf Real Jan 29 '15 at 17:29