Install a Listener into a Button

Press the button to change its text. A button is an example of an input control. Every input control should be at least 48dp × 48dp, and Android wants buttons to be ALL UPPERCASE (see Style and android:textAllCaps). The button won’t do anything unless we plug a listener object into it.

The onCreate method of our MainActivity creates a listener and implants it into the button. Our listener is a MyOnClickListener. It implements the interface View.OnClickListener, which is one of the types of listener that can be put into into a button. The onClick method of our listener overrides the abstract method onClick of View.OnClickListener.

Source code in Button.zip

  1. MainActivity.java contains the new class MyOnClickListener as well as the old class MainActivity. MyOnClickListener can be defined in this .java file because it is not a public class. The string literal in "double quotes" should have been string resource in strings.xml, but I was lazy.

  2. activity_main.xml. I replaced the TextView in the RelativeLayout with a Button. I added the attributes android:id="@+id/button", android:layout_centerInParent="true", and android:textAllCaps="false" to the Button.

  3. strings.xml. I added a string resource named button_text.

  4. R.java creates the int variables R.layout.activity_main and R.id.button.

  5. AndroidManifest.xml.

  6. build.gradle (Module: app)

Documentation

  1. Buttons in the Material Design spec.
  2. Buttons and other input controls in the User Interface guide.
  3. The Java class android.widget.Button
    1. Documentation
    2. platform_frameworks_base/core/java/android/widget/Button.java source code on GitHub

Buttons in ApiDemos

  1. Views → Buttons
    Three buttons that don’t do anything when pressed.
    1. ApiDemos/src/com/example/android/apis/view/Buttons1.java
      Class Buttons1 is a subclass of class Activity.
    2. ApiDemos/res/layout/buttons_1.xml
      creates a LinearLayout containing two Buttons and a ToggleButton. For the question mark in the style="?android:attr/buttonStyleSmall" attribute of the second Button, see Referencing style attributes and Styles and Themes.
    3. ApiDemos/res/values/strings.xml contains three string resources:
      1. buttons_1_normal
      2. buttons_1_small
      3. buttons_1_toggle. This string is not displayed by the ToggleButton.

  2. Views → Controls → 1. Light Theme
    Controls that do nothing.
    1. ApiDemos/src/com/example/android/apis/view/Controls1.java
      Class Controls1 is a subclass of class Activity.
    2. ApiDemos/res/layout/controls_1.xml
      creates a LinearLayout containing two Buttons and a ToggleButton. For the question mark in the style="?android:attr/buttonStyleSmall" attribute of the second Button, see Referencing style attributes and Styles and Themes.
    3. ApiDemos/res/values/strings.xml contains three string resources
    4. ApiDemos/AndroidManifest.xml gives a different android:theme attribute to the activities view.Controls1, view.Controls2, etc.

  3. Views → ImageButton
    More buttons that don’t do anything, but at least they contain pictures.
    1. ApiDemos/src/com/example/android/apis/view/ImageButton1.java
      Class ImageButton1 is a subclass of class Activity.
    2. ApiDemos/res/layout/image_button_1.xml
      creates a LinearLayout containing the ImageButtons.
    3. The files sym_action_call.png, sym_action_chat.png, and sym_action_email.png are in the directories
      ~/Library/Android/sdk/platforms/android-23/data/res/drawable-ldpi, -mdpi, -hdpi, and -xhdpi.

Things to try

  1. The onClick method of the MyOnClickListener cannot create a piece of Toast, because it has no Context (such as an Activity object) that it can pass to Toast.makeText.

    To fix this, let the listener class be an anonymous inner class (i.e., a nameless nested class) inside of the onCreate method class MainActivity. Remove the existing class MyOnClickListener and change the onCreate method to the following. Since the listener class is non-static, it can say MainActivity.this.

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button button = (Button)findViewById(R.id.button);
    
            View.OnClickListener onClickListener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Button button = (Button)v;	//downcast
                    button.setText("Thanks for pressing this button.");
                    Toast.makeText(MainActivity.this, "Thanks.", Toast.LENGTH_LONG).show();
                }
            };
    
            button.setOnClickListener(onClickListener);
        }
    

  2. Let the listener object be an anonymous object. It will be an anonymous object belonging to an anonymous class. Change the onCreate method of class MainActivity to the following.
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button button = (Button)findViewById(R.id.button);
    
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Button button = (Button)v;	//downcast
                    button.setText("Thanks for pressing this button.");
                    Toast.makeText(MainActivity.this, "Thanks.", Toast.LENGTH_LONG).show();
                }
            });
        }
    

  3. In the above programs, the Activity and the View.OnClickListener were two separate objects. Combine them into a single object which will do the work of both. Change class MainActivity to the following. It’s faster and smaller, and it makes it easier for the listener to access the fields of the Activity (because the listener is the Activity). The other objects in the app might appreciate a listener that is a big, stable, publicly visible object. The downside is that the Activity will always be stuck with the same onClick method.
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button button = (Button)findViewById(R.id.button);
            button.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            Button button = (Button)v;	//downcast
            button.setText("Thanks for pressing this button.");
            Toast.makeText(this, "Thanks.", Toast.LENGTH_LONG).show();
        }
    
    }
    

  4. Make two buttons. You could implant a different listener object into each button, or even listener objects of two different classes, but we’ll give the same listener to both of them. The listener will be the Activity object. Change activity_main.xml to the following. At the point where each id is first mentioned, it must be created with a plus sign. At each subsequent mention of the id, do not write a plus sign.
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity" >
    
        <Button
            android:id="@+id/button0"
            android:layout_centerInParent="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAllCaps="false"
            android:text="Press in case of emergency" />
    
        <Button
            android:id="@+id/button1"
            android:layout_below="@id/button0"
            android:layout_centerHorizontal="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAllCaps="false"
            android:text="Press when all clear" />
    
    </RelativeLayout>
    

    onClick can examine the text of the button to tell which button was clicked:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button button0 = (Button)findViewById(R.id.button0);
            button0.setOnClickListener(this);
            Button button1 = (Button)findViewById(R.id.button1);
            button1.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            Button button = (Button)v;
            CharSequence charSequence = button.getText();
            String string = charSequence.toString();
            String[] words = string.split("\\s+");	//white space
            String lastWord = words[words.length - 1];
    
            if (lastWord.equals("emergency")) {
                Toast.makeText(this, "Remain calm.", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(this, "You can relax now.", Toast.LENGTH_LONG).show();
            }
        }
    
    }
    

  5. What happens if you remove the android:textAllCaps attribute from the Button element in activity_main.xml? What sinister force is trying to set the text to all caps?

  6. Toggle the Button’s text. Add the following field to the View.OnClickListener class.
                boolean toggle = false;
    
    Then change the setText to
                    toggle = !toggle;		//exclamation point means "the opposite of"
                    button.setText(toggle ? "odd" : "even");
    

  7. Let’s be professionals and put all the strings where they belong, in the resource file res/values/strings.xml. Add the following string resource to that file. setText will accept any CharSequence, including the String returned by getString.
        <string name="button_thanks">Thanks for pressing this button.</string>
    
    In onClick, change the call to setText to the following.
                    button.setText(getString(R.string.button_thanks));
    

  8. Let activity_main.xml install the listener into the Button. In this case, the listener will have to be the Activity object, and the Activity object does not need to implement the interface View.OnClickListener. Add the following attribute to the Button element.
            android:onClick="onClick"
    
        //No @Override.
        public void onClick(View view) {
            Button button = (Button)view;	//downcast
            button.setText(getString(R.string.button_thanks));
        }
    

  9. Have the Button move us to a new screen. First, create the file second_screen.xml in the folder app/res/layout, which already contains the file activity_main.xml. In the Android Studio project view, select the folder app/res/layout. Pull down
    File → New → Layout resource file.
    New Resource File
    File name: second_screen
    Root element: LinearLayout (or RelativeLayout, whichever one you want)
    OK
    Edit your new file second_screen.xml to contain the following.
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Second screen"/>
    
    </LinearLayout>
    
    The method that is called when we press the Button should say
            setContentView(R.layout.second_screen);