Best practice for defining button events in android

AndroidButtonEvent Handling

Android Problem Overview


I have a Layout defined in XML which consists of several Buttons.

Currently I am doing this in the OnCreate method to define the event handlers against the buttons:

public void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
        
    Button newPicButton = (Button)findViewById(R.id.new_button);
    newPicButton.setOnClickListener(btnListener);
    ..... similarly for other buttons too
    .....
}

Inside of the Button's onClick event, I launch a camera Intent to get a picture and inside the onActivityResult callback I am again setting the event handlers along with setting the View like this:

protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{ 
    setContentView(R.layout.main);
    Button newPicButton = (Button)findViewById(R.id.new_button);
    newPicButton.setOnClickListener(btnListener);
    ...similarly for other buttons too
}

I am new to android and this approach of redefining an event every time seems quite dirty to me. I would like to know what is the best practice in terms of defining button event handlers in scenarios like this.

Edit: pasting my complete class

public class CameraAppActivity extends Activity 
{
    /** Called when the activity is first created. */
	
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button newPicButton = (Button)findViewById(R.id.new_button);
        newPicButton.setOnClickListener(btnListener);
    }
    
    //---create an anonymous class to act as a button click listener---
    private OnClickListener btnListener = new OnClickListener()
    {
    	
        public void onClick(View v)
        {   
        	 //Intent newPicIntent = new Intent(v.getContext(), NewPictureActivity.class);
             //startActivityForResult(newPicIntent, 0);
        	Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        	startActivityForResult(cameraIntent, 999);
        } 
        
    };  
    
    protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    {  
       
        setContentView(R.layout.main);
        Button newPicButton = (Button)findViewById(R.id.new_button);
        newPicButton.setOnClickListener(btnListener);
       
       //if I comment last two lines nothing happens when I click on button
        
    }  

The main question is

setContentView(R.layout.main);
Button newPicButton = (Button)findViewById(R.id.new_button);
newPicButton.setOnClickListener(btnListener);

Re-registering events inside onActivityResult.. is it right approach? Or am I doing something wrong? Because If I don't re-register event nothing happens when I click the button.

Android Solutions


Solution 1 - Android

Why not registering onClick event in the XML layout and then handle it in the code. This is how I would do it:

<Button
android:id="@+id/my_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click me"
android:onClick="onBtnClicked">
</Button>

and now create a method that would handle clicks

public void onBtnClicked(View v){
    if(v.getId() == R.id.my_btn){
        //handle the click here
    }
}

Alternatively, you can set the OnClickListener individually for each item in the code. Then use the if/else or switch statements to determine the origin.

This way you can have one method that handles all buttons from one layout.

UPDATE:
Although this is a valid approach I would strongly recommend the second option. It's cleaner and easier to maintain especially when you work with fragments.

Solution 2 - Android

Here is the best approach with code:

  public class MyTest extends Activity implements OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
     //... some other code here to init the layout
        Button btn1 = (Button)findViewById(R.id.button1);
        Button btn2 = (Button)findViewById(R.id.button2);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.button1:
                break;
            case R.id.button2:
                break;
        }
    }
}

The new class with an interface is only good if you want to decouple the implementation (when you want to use the same class code somewhere else, move it to another seperate class file etc..) but in general if you are doing things connected with the current activity you are on and the onClick implementations depend on it running with reference to the objects defined there you should definitely use the method i suggested.

Creating class interfaces is only good when you want to achieve communication between seperate classes or activities and keep things apart. other than that its a bad practice creating subclasses for this.

Solution 3 - Android

this is the best approach

@Override
public void onCreate(Bundle savedInstanceState) {
        button1.setOnClickListener(onClickListener);
        button2.setOnClickListener(onClickListener);
        button3.setOnClickListener(onClickListener);
}

private OnClickListener onClickListener = new OnClickListener() {
     @Override
     public void onClick(final View v) {
         switch(v.getId()){
             case R.id.button1:
                  //DO something
             break;
             case R.id.button2:
                  //DO something
             break;
             case R.id.button3:
                  //DO something
             break;
         }

   }
};

Solution 4 - Android

There is no best practice defined. It heavily depends on the use case. You could define them in your XML layout using the Button's onClick attribute.

XML example:

<!-- Stuff -->
<Button android:id="@+id/my_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Click Me!"
    android:onClick="myClickMethod" />

Java example:

// stuff
public void myClickMethod(View v) {
    // more stuff
}

That way you don't have to implement the OnClickListener yourself. You can assign each Button the same onClick method and then simply decide on a per-view basis what action to trigger or you can have a separate method for every Button.

In general I advice against using one OnClickListener for more than one Button. It is easier to understand what each listener is supposed to do if you use descriptive names which is what you should do anyway.

Solution 5 - Android

I like the "modern" DI way by using Butter Knife:

  1. Declare your view

@InjectView(R.id.buttonAlert) 
Button buttonAlert;

2. Inject all annotated resources

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

3. Annotate and implement your onClick method

@OnClick(R.id.buttonAlert)
public void alertClicked(View v){
    // your logic
}

Solution 6 - Android

@Hasan This is the best approach that I have found and which works for me every single time flawlessly.

  • In Layout.xml define the onClick for the button

    <Button android:id="@+id/Button01"			
       android:onClick="@string/method" 
       android:focusable="true"
       android:clickable="true"
    >
    </Button>
    
  • In the R.string file add the following line

    string name="method">buttonFunction</string
    
  • In the sample.java file the function define in R.string will be called on the click of the button and it should look something like

    public void buttonFunction(View view) {
        // do nething u like on the click of the button
    }
    

Solution 7 - Android

Your Activity should implement OnClickListener and you should write all your event handling for all buttons inside single OnCLick() method.

Solution 8 - Android

The problem is the object for the button newPicButton is being created as a local object which is valid only in the scope of the function onCreate and as soon as the code exits that function the garabage collector deallocates the object for the button. what you need to do is declare the newPicButton object outside any method and then in onCreate method assign it a listener. This will solve your problem and i hope i have explained why nothing happens when you remove the code for the newPicButton in the onActivityResult method :)

Solution 9 - Android

I know this is old, but if anyone is wondering why you cannot add onClickListener from onActivityResult, that is because the button is null. If you initialize it once again (just as you did in onCreate), you can add the listener. Be careful, though, everything else will be null as well, so if you are taking data from an EditText, for example, you have to initialize that, as well (a simple check if the object is null in the lisneter will do the trick).

Solution 10 - Android

Here is how I did it:

  1. In Buttons XML file set android:onClick="onClick".
  2. In your Activity implement View.OnClickListener.
  3. Inside onClick method use Switch key (as below).

@Override

public void onClick(View view) {
    Intent intent;
    switch(view.getId()){

        case R.id.home_button:
            //DO something
            intent = new Intent(HashTagActivity.this,MainActivity.class);
            startActivity(intent);
            break;
        case R.id.back_button:
            //DO something
             intent = new Intent(HashTagActivity.this,ClassActivity.class);
            startActivity(intent);
            break;
        case R.id.favorite_button:
            //DO something
            break;
        case R.id.copy_button:
            //DO something
    
            break;
        
    }
}

It works Great.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHaris HasanView Question on Stackoverflow
Solution 1 - AndroidMarqsView Answer on Stackoverflow
Solution 2 - AndroidDArkOView Answer on Stackoverflow
Solution 3 - AndroidNguyen Minh BinhView Answer on Stackoverflow
Solution 4 - AndroidOctavian A. DamieanView Answer on Stackoverflow
Solution 5 - AndroidfuncoderView Answer on Stackoverflow
Solution 6 - AndroidNitinView Answer on Stackoverflow
Solution 7 - AndroidSujitView Answer on Stackoverflow
Solution 8 - AndroidNitinView Answer on Stackoverflow
Solution 9 - AndroidYordan LyubenovView Answer on Stackoverflow
Solution 10 - AndroidDr. Salah aldin Mahmoodi nejadView Answer on Stackoverflow