4

I have a problem with my Dialog Fragment. I wanted to use android:onClick attribute as in my opinion code is more clear then.

In my layout I have the following declaration:

<Button
        android:id="@+id/dialog_new_database_button_cancel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="@string/button_cancel"
        android:maxLines="1"
        style="?android:attr/buttonBarButtonStyle"
        android:onClick="buttonCancel"
        />

Now my DialogFragment

import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class DialogNewDatabase extends DialogFragment {

    public DialogNewDatabase() {
        // Empty constructor required for DialogFragment
        super();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView (inflater, container, savedInstanceState);
        View view = inflater.inflate(R.layout.dialog_new_database, container);    
        getDialog().setTitle("Hello");
        return view;
    }

    @Override
    public void onCreate(Bundle bundle) {
        setCancelable(true);
        setRetainInstance(true);
        super.onCreate(bundle);

    }

    @Override
    public void onDestroyView() {
        if (getDialog() != null && getRetainInstance())
            getDialog().setDismissMessage(null);
        super.onDestroyView();
    }

    public void buttonCancel (View view) {
        dismiss();
    }

    public void buttonOK (View view) {

    }

}

I now when I try to click cancel button I get:

java.lang.IllegalStateException: Could not find a method buttonCancel(View) in the activity class android.view.ContextThemeWrapper for onClick handler on view class android.widget.Button with id 'dialog_new_database_button_cancel'
at android.view.View$1.onClick(View.java:3031)
at android.view.View.performClick(View.java:3511)
at android.view.View$PerformClick.run(View.java:14105)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4482)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:787)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:554)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NoSuchMethodException: buttonCancel [class android.view.View]
at java.lang.Class.getConstructorOrMethod(Class.java:460)
at java.lang.Class.getMethod(Class.java:915)
at android.view.View$1.onClick(View.java:3024)
... 11 more

Any idea? Is that perhaps somehow related with the fact I use import android.support.v4.app.DialogFragment (support v4)? How to solve that (I still would prefer to use android:onClick in xml layout).

user2707175
  • 1,133
  • 2
  • 17
  • 44
  • Related [SO question](http://stackoverflow.com/q/18267288/1051783) – gunar Oct 15 '13 at 12:34
  • @gunar It doesn't solve the problem. That discussion suggest only that managing onClick when you use Fragments is not possible. – user2707175 Oct 15 '13 at 13:03
  • Your problem is that you're using `onClick` attribute. In that link I am highlighting why this attribute shouldn't be used. But if you're stubborn and want to use it, in this case you'll have to delegate the action from Activity to `DialogFragment`, just as in reis_eliel below answer. – gunar Oct 15 '13 at 13:11
  • @gunar Thank's a lot for help. I will probably use traditional setOnClickListener(new Button.OnClickListener() { ... but I really don't understand why google add some things which doesn't work. ( – user2707175 Oct 15 '13 at 13:25
  • They work, but you don't know how to use them :P ... bytheway, mark your questions anwers as accepted. Otherwise people will stop answering you. – gunar Oct 15 '13 at 13:26
  • @gunar, As for now nobody is not able to answer how to use android:onClick in my case. This suggest that it works in some cases but it doesn't in others. In my opinion, it suggest that simply something was badly designed. – user2707175 Oct 15 '13 at 14:35
  • user2707175 - I totally understand you. I CANNOT FIND ANY WAY TO USE onClick. onClick works, for example, perfectly ON MY MAIN ACTIVITY WITH BUTTON ON LIST VIEW CELLS. But I can't get it to work here! – Fattie May 24 '14 at 18:04

3 Answers3

4

I would try a different approach which works fine for me:

  1. implement an OnClickListener into your fragment:

    public class DialogNewDatabase extends DialogFragment implements OnClickListener`
    
  2. have a button with an unique id in xml, which does NOT need android:clickable

    <Button  android:id="@+id/dialog_new_database_button_cancel" />
    
  3. override the method onClick() within your fragment and insert a reaction on your click:

    public void onClick(View v) {       
      switch (v.getId()) {
        case R.id.dialog_new_database_button_cancel:
            // your stuff here
            this.dismiss();
    
            break;          
        default:                
            break;
       }
    }   
    
  4. import the necessary:

    import android.view.View.OnClickListener; 
    
  5. start the onClickListener on the button:

    private Button bCancel = null;
    bCancel = (Button) findViewById(R.id.dialog_new_database_button_cancel);
    bCancel.setOnClickListener(this);
    // it is possible that you might need the refrence to the view.
    // replace 2nd line with (Button) getView().findViewById(...);
    

    This way you can handle even more clickable buttons in the same onClick-method. You just need to add more cases with the proper ids of your clickable widgets.

bofredo
  • 2,348
  • 6
  • 32
  • 51
  • Sorry it doesn't work. I put breakpoint on switch and when debugging it even doesn't stop here. Something is missing? – user2707175 Oct 15 '13 at 14:56
  • Perhaps import android.content.DialogInterface.OnClickListener; listener should be used? With public void onClick(DialogInterface dialog, int position) method? – user2707175 Oct 15 '13 at 14:59
  • yes of cause. I thought the import was obvious. it is: import android.view.View.OnClickListener; – bofredo Oct 15 '13 at 15:10
  • 1
    No way, neither with import android.view.View.OnClickListener, nor with android.content.DialogInterface.OnClickListener it doesn't work. Debugger doesn't stop at the beginning of the method. I even put Toast message wondering if my debugger works ok. No way. The only method which works is: cancel.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { dismiss(); } }); (and yes, when trying to use onClick method I marked as comment the lines in the code for the previous method. Quite suprising isn't it.... – user2707175 Oct 15 '13 at 15:15
  • oh dear, sorry i've been using that too often to see what you didn't get. You will have to start the OnClickListener programmatically too. I will add #5 now :) – bofredo Oct 15 '13 at 15:22
  • I tried also to add "view.setOnClickListener(this);" in my "public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {". No way. Still it doesn't work. :( – user2707175 Oct 15 '13 at 15:25
  • 1
    Now it works like a dream. THank you. But I'm still curious why android:onClick attribute doesn't work. :( – user2707175 Oct 15 '13 at 17:13
  • It works too, but I think it is a little bit limited in its usages. I recommend using programmatic listeners. But im glad i could help ya :) – bofredo Oct 15 '13 at 18:07
  • 1
    @bofredo thanks for the incredibly useful code extracts! I sent a bounty to say thanks. Cheers. – Fattie May 25 '14 at 19:03
2

I don't think that is related to the support fragment.

The issue seems to arise from the fact that you are registering a onClick on XML that fires up based on the activity that the fragment was binded at the time of the click.

As your "buttonCancel" method does not exists in the activity (because it is inside the fragment), it fails.

I don't think that really is a desirable solution, but you can register your "buttonCancel" method on your activity for that error to go away, and make that "buttonCancel" method registered on the activity only call the method that exists in the fragment, in case you want to keep your action / view behaviour inside the fragment.

reis_eliel
  • 338
  • 2
  • 7
  • To be precise we have the following construction Activity with ViewPager/Fragments/ and next this DialogFragment is run. I was sure that android:onClick is linked to the DialogFragment and basing on your answer I understand Android tries to find it in main Activity? Hard to believe that it was designed like this. – user2707175 Oct 15 '13 at 12:53
  • I made a small test, I added public void buttonCancel (View view) method to the main activity. But the problem repeated - still the same exception. Should perhaps my DialogFragment class implement any interface? – user2707175 Oct 15 '13 at 12:56
  • I put also my buttonCancel method within my Fragment class. No way. ( – user2707175 Oct 15 '13 at 13:08
  • Well, a few other things: Was your activity method public? Was it on the activity that hosts the fragment? Is your project API lvl 1.6+ ? – reis_eliel Oct 15 '13 at 13:14
  • Yes method wa public. android:minSdkVersion="14" android:targetSdkVersion="18" – user2707175 Oct 15 '13 at 13:22
  • Well, other then the fact that you are using a DialogFragment, i don't see anything that could cause this unwanted behaviour. Can you please try with a normal Fragment class and see if it works? Maybe because the DialogFragment is supposed to be self contained click-handler, it does not allow the XML pass of clicks like that. But its just a wild wild guess. – reis_eliel Oct 15 '13 at 14:45
  • Yeah, I'm guessing also that it's somehow related with DIalogFragment and Fragment. But I spent 4-5 hours on playing with such a simple thing as buttons, and no way to spend more. I programmed in many different languages/environment but I've never seen such unfriendly and inconsistent solution as Android. :(( (Although I have to admit that in the latest years I was focused on other things thus Android programming is returning to programming after few years). – user2707175 Oct 15 '13 at 20:31
-1

Try:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView (inflater, container, savedInstanceState);
    View view = inflater.inflate(R.layout.dialog_new_database, container);    
    getDialog().setTitle("Hello");
    return view;

    private void buttonCancel (View view) {
    dismiss();
    }
    private void buttonOK (View view) {
    }
}
belosand
  • 129
  • 3
  • 14