1

On Android here is for example an excellent code fragment,

showing how to achieve five buttons on a dialog fragment...


android DialogFragment android:onClick="buttonCancel" causes IllegalStateException could not find a method

in your DialogFragment, you have to setOnClickListener(this) for all of your buttons/imageviews etc.

Then you implement View.OnClickListener and have a routine like this...

public void onClick(View v)
    {
    Utils.Log("Fucking 'A' sort of... ");
    switch (v.getId())
        {
        case R.id.postfragment_send:
            break;
        etc etc etc
        default:
            break;
        }
    }

That's all fantastic. BUT.


Over in my main activity, where I have a ListView. The custom cells have five buttons. Very simply, in the main activity, I have five routines named whatever I like...

public void clickedComments(View v)
    {
    int position = feed.getPositionForView(v);
    ...etc etc
    }
public void clickedExplosions(View v)
    {
    int position = feed.getPositionForView(v);
    ...etc etc
    }
public void clickedTanks(View v)
    {
    int position = feed.getPositionForView(v);
    ...etc etc
    }

Then you just do this which is unbelievably easy ("screw Xcode!") ...

enter image description here

Amazing!


My question, why can't I use the 'onClick system' in dialog fragments?

What am I doing wrong? Can an android expert explain what the fundamental difference is between the two? For the record my projects are 4.1+ only.

Thanks!!


Here I am pasting in a full example of a fragment, using the first method described above.

public class HappyPopupFragment extends DialogFragment implements View.OnClickListener
    {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
        {
        Dialog dialog = super.onCreateDialog(savedInstanceState);

        dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        dialog.getWindow().setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
        dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);

        return dialog;
        }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
        View view = inflater.inflate(R.layout.happy_popup, container);
        _setupButtons(view);
        return view;
        }

    public void onClick(View v)
        {
        Utils.Log("Fucking 'A' sort of... ");
        switch (v.getId())
            {
            case R.id.button_a:
                Utils.Log("tanks !!");
                break;

            case R.id.button_b:
                Utils.Log("bombs !!");
                break;

            case R.id.button_c:
                Utils.Log("guns !!");
                break;

            case R.id.button_d:
                Utils.Log("ammo !!");
                break;

            default:
                break;
            }
        }

    private void _setupButtons(View view)
        {
        ((ImageView)view.findViewById(R.id.button_a)).setOnClickListener(this);
        ((ImageView)view.findViewById(R.id.button_b)).setOnClickListener(this);
        ((ImageView)view.findViewById(R.id.button_c)).setOnClickListener(this);
        ((TextView)view.findViewById(R.id.button_d)).setOnClickListener(this);
        }

    }
Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 1
    It's just a fact of Android DialogFragment API. Callback methods defined in a fragment XML layout are called in the Activity which contains this fragment. It`s more simple than you mean because in a previous Android API-s such "XML defined" callbacks were called in activities also. – DenisMath May 24 '14 at 18:41
  • @Dennis .. awesome ... ah, are you saying that if I set those "onClick" in the five buttons in the fragment xml .. in fact .. they would be called ON MY MAIN ACTIVITY?? (Indeed, I guess I would have to have such methods present, in the activity . right?) Wow! – Fattie May 24 '14 at 18:45
  • 1
    Yes, you are absolutely right! – DenisMath May 24 '14 at 18:51

3 Answers3

2

that's actually a pretty simple answer, but you have to start it by remembering that Android 1.0 there were no Fragments.

First let's see what an activity really is:

java.lang.Object
   ↳    android.content.Context
       ↳    android.content.ContextWrapper
           ↳    android.view.ContextThemeWrapper
               ↳    android.app.Activity

An object that extend Context, that's what it is.

So, when you inflate the XML layout, that method inflate is doing stuff like creating and setting up the views like that:

View v = new View(context) // see the context here ?

then when you set on your XML onClick:commentsClick for example, what is happening when you click is:

getContext().commentsClick(View.this); // look, the context again

so let's analyse that:

The XML onClick tries to call back to the context, meaning, call back to the activity that inflated it. See that the IllegalStateException message says that it cannot find the method? Well, because it's not in the activity. Probably if you put commentsClick in the Activity that is creating the DialogFragment, it will work, but that's just bad O.O., right?

The thing with the XML onClick was a nice facilitator to avoid on the huge switch(int) case, but it is a solution that simply does not scale to other classes that might inflate layouts, such as Fragments.

Budius
  • 39,391
  • 16
  • 102
  • 144
  • holy crap! this is amazing. whoa. (I cannot wait to have more than one activity!) So what I wonder is: notice the class I just pasted in. Notice the "repetitive" code in _setupButtons. And the repeteitive long switch statement. In fact - **is that just what you guys do? that is totally cool?** There's no problem with that - that's solid bolierplate? I haven't missed something more elegant? THANK YOU, EXPERTS!!!! – Fattie May 24 '14 at 20:28
  • @JoeBlow Nah. That's really that. **solid boilerplate** as you perfect described. As I said, the XML `onClick` is a really elegant solution that is impossible to scale to outside the activity. There're a great annotation libraries (thanks Jake Wharton) helps on that https://github.com/JakeWharton/butterknife – Budius May 24 '14 at 22:08
  • **But wait** .. is it true that if I'm in a dialogFragment, and I go "getActivity()" ... I'm simply getting the same "main activity" of the main activity which launched this fragment? thanks!!! – Fattie May 27 '14 at 13:09
  • yes. Any fragment `getActivity()` will , ... well.... get the activity that originally launched that fragment. – Budius May 27 '14 at 13:50
  • incredible. I foolishly thought fragments had their own activity. The next leap for me has got to be understanding .. activities! :) I found a good article. http://apcmag.com/working-with-multiple-activities.htm Android is great. It's a fantastic change from iOS and Unity. Thank you so much once again. – Fattie May 27 '14 at 14:36
  • so then guess what happen when you call `getActivity()` before `onAttach(Activity)` or after `onDetach()`.... – Budius May 27 '14 at 14:53
  • Budius! I hear you man! You'll be amazed to hear: I do check first, if getActivity() returns null. Can you believe it? :) I don't yet totally understand those situations; but I safely "give up and do nothing harmlessly" if it's null. Since presently I have only the main activity in the app, I'm hoping I'm reassembly safe. THANK YOU SO MUCH – Fattie May 27 '14 at 14:56
1

you can use onClickListener, on each View and its subclasses.

setOnClickListener takes as parameter an instance of the class that implements View.OnclickListener.

If you have an error on setOnClickListener(this) it means that the object this refers is an object of an class that does not implements View.OnClickListener


In other words...

Here's how to make onClickListener work for custom cells in custom list views in custom dialog fragments!

in the ADAPTER class (1) for your list view, you'll have code that sets the values for each cell. (Setting text and os on.) In fact, in that same code set onClickListener for each cell button:

v.nameTV.setText( "User Name" );
v.inviteIV.setOnClickListener( ourBoss ); // like this

the problem is what to set the listener to. In fact you want it to be your dialog fragment. "ourBoss" will be the DialogFragment. So (2) when the dialog fragment creates the adapter, pass it in:

in the dialog fragment creating the adaptor:

fosAdapter = new YourHappyAdapter(
   getActivity(), getActivity().getLayoutInflater(),
   otherStuff, otherStuff, this);

and in the adapter itself ...

public class YourHappyAdapter extends BaseAdapter
  {
  YourDialogFragmentClass ourBoss;

  public FosAdapter(
        Context context, LayoutInflater inflater,
         blah, blah,
         YourDialogFragmentClass ydfc)
        {
        blah
        blah

        ourBoss = ydfc;
        }

Finally then (3) in the ordinary way, in YourDialogFragmentClass, you can have the usual onClick code!! Hooray, you're done!!

  public void onClick(View v)
    {
    switch (v.getId())
      {
      case R.id.submit_button: // from the overall fragment screen
        _searchNow();
        break;

      case R.id.cell_button:      // that one's from a cell.
        Utils.Log("IT WORKED !!!!!!!!!!!!!!!!! !!");
        userWantsANewTank(v);
        break;

      default:
        break;
      }
    }

It's (essentially) just not realistic to use the "old-fashioned" xml-handy-onClick method, when doing custom tables and custom dialog fragments!

Hope it helps someone!

Fattie
  • 27,874
  • 70
  • 431
  • 719
Blackbelt
  • 156,034
  • 29
  • 297
  • 305
  • That's hard for me to follow; are you saying I can change the "destination" of the onClick items? mfg – Fattie May 24 '14 at 18:47
  • Ahhhhhhh .. I've totally figured it out. Blackbelt, I added a few words for beginners on your answer, thank YOU!!! – Fattie May 27 '14 at 13:36
1

It's just a fact of Android DialogFragment API. Callback methods defined in a fragment XML layout are called in the Activity which contains this fragment. It`s more simple than you mean because in previous Android API-s such "XML defined" callbacks were called in activities also.

(Transferred from my comments as it strikes me as an answer to the question of topic. Perhaps this will be more convenient to future readers of this topic.)

DenisMath
  • 547
  • 4
  • 8
  • Dennis, run that by me again. Currently, "xml defined" callbacks are called in **the Activity which contains this fragment**. But then you explain that in old versions, "xml defined" callbacks were called "in activities also" ... did you mean .. ? – Fattie May 24 '14 at 20:14
  • There is no hidden meaning, only fails of my english grammar skills, sorry! I mean that in previous API-s "XML defined" callbacks were called in activities and this status quo preserves in the recent versions of Android with respect to DialogFragment API. – DenisMath May 24 '14 at 20:28
  • Ahhhhhh!!! This is AWESOME! So in fact, as I was asking Budius. If you look at my _setupButtons() example code -- **that is JUST WHAT YOU DO**. is that right? that's perfectly ok - you'd do that, in a modern project (today) as an expert? Thanks!! – Fattie May 24 '14 at 20:30