user316117
user316117

Reputation: 8281

Runtime equivalent to (XML) android:onClick?

One way to implement a button handler in Android since 1.6 is to put this in the XML where the button is declared . . .

...
android:onClick="TheButtonHandler"
...

and then just put the handler routine in the Activity class . . .

   public void TheButtonHandler(View target) {
    // ... button handler code
 }

I have some Android code where all the buttons are created in Java at runtime during initialization, not in XML, but I want to do the same stuff as the above XML including specifying the button handler. N.B. that I want the button handlers to be in their own separate routines just as they would if I were declaring them in the XML in the above example, I don't want button handler code embedded in the my initialization code.

I've seen examples of using setOnClickListener, but the handler code was always embedded inline, e.g.,

  demoButton.setOnClickListener(new Button.OnClickListener(){
  @Override
  public void onClick(View arg0) {
               // ... button handler code     
  }});

... which is where I don't want it because that would put the button handler code up where the buttons are initialized. What's the Java runtime equivalent of android:onClick ?

Upvotes: 2

Views: 213

Answers (4)

Nantoka
Nantoka

Reputation: 4243

As @laalto already pointed out, Android View itself simply takes the name of the handler and uses Java reflections to create an OnClickListener object. I digged the code out from View.java and wrapped it in a separate object:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.Activity;
import android.view.View;

public class ViewOnClickHandler implements View.OnClickListener{

    Activity mActivity;
    Method mHandler;
    ViewOnClickHandler(Activity activity, View view, String handlerName){
        mActivity = activity;
        try {
            mHandler = activity.getClass().getMethod(handlerName, View.class);
        } catch (NoSuchMethodException e) {
            int id = view.getId();
            String idText = id == View.NO_ID ? "" : " with id '"
                    + mActivity.getResources().getResourceEntryName(id) + "'";
            throw new IllegalStateException("Could not find a method " +
                    handlerName + "(View) in the activity "
                    + activity.getClass() + " for onClick handler"
                    + " on view " + view.getClass() + idText, e);
        }           
    }
    @Override
    public void onClick(View v) {
        try {
            mHandler.invoke(mActivity, v);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Could not execute non "
                    + "public method of the activity", e);
        } catch (InvocationTargetException e) {
            throw new IllegalStateException("Could not execute "
                    + "method of the activity", e);
        }
    }

    static void set(Activity activity, View view, String handlerName){
        view.setOnClickListener(new ViewOnClickHandler(activity, view, handlerName));

    }
}

If you put this code in a package of your choice and import the class into your activity, the code from your sample can shrink down to:

ViewOnClickHandler.set(this, demoButton, "TheButtonHandler");

Upvotes: 0

TMH
TMH

Reputation: 6246

Method 1

public class YourActivity extends Activity implements View.OnClickListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //....

        myButton1.setOnClickListener(this);
        myButton2.setOnClickListener(this);
        myButton3.setOnClickListener(this);
        myButton4.setOnClickListener(this);



        myButton1.setTag(0);
        myButton2.setTag(1);
        myButton3.setTag(2);
        myButton4.setTag(3);
    }

    @Override
    public void onClick(View v) {
        //Do your stuff
        switch ((int) v.getTag()) {

            case 0:
                //First button pressed;
                break;
            case 1:
                //Second button pressed;
                break;
            case 2:
                //Third button pressed;
                break;
            case 3:
                //Fourth button pressed;
                break;
            default:
                //Unknown value, handle an error
                break;
        }
    }
}

Method 2

public class YourActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //....

        myButton1.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View arg0) {
               // ... button handler code     
            }
        });

        myButton2.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View arg0) {
               // ... button handler code     
            }
        });

        myButton3.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View arg0) {
               // ... button handler code     
            }
        });

        myButton4.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View arg0) {
               // ... button handler code     
            }
        });
    }
}

Upvotes: 2

laalto
laalto

Reputation: 152867

The equivalent is setOnClickListener() and View.OnClickListener.

If you examine the platform source, you'll see that methods defined with onClick attribute are transformed to a an OnClickListener that invokes the named method via Java reflection.

I've seen examples of using setOnClickListener, but the handler code was always embedded inline ... which is where I don't want it because that would put the button handler code up where the buttons are initialized

You can have your OnClickListeners e.g. as member variables like this:

private OnClickListener mClickListener = new OnClickListener() { ... };

//

    view.setOnClickListener(mClickListener);

to have the initialization and click handling in separate places.

Upvotes: 2

Jay Snayder
Jay Snayder

Reputation: 4338

Well you don't have to include them inline if that is what you are requesting. You can create your own class such as:

MyButtonClickListener implements Button.OnClickListener
{
  ...
}

and this can be defined elsewhere. Then you can reference it in the initialization code similarly but not taking up as much space for readability as such.

demoButton.setOnClickListener(new MyButtonClickListener());

if that is indeed your question.

Upvotes: 1

Related Questions