1

Right now i'm having :-

1) 1 activity which is the main activity that extends from AppCompactActivity.

2) 1 fragment class that extends from fragment, this is the fragment that being called from main activity (1) - ProfileTeacherActivity.java

3) 1 fragment class that extends from DialogFragment, this dialog getting called from fragment (2) - ModalBox.java

So, basically, this is just a simple flow of execution. At start, the applications showing the main activity (1) having drawer that have a few links as example a profile link, when click this link, the application call the fragment (2) showing details of profile with one edit button. After clicking edit button, the applications will invoke DialogFragment (3) that contains some of EditText for editing user's profile.

What i want to achieve is, after editing user's profile and successful saved into database, i tried to send user's data back to fragment (2) just to show latest updated info, unfortunately it didn't work.

Here is what i'm tried :

1) Creating Interface inside DialogFragment (3) - ModalBox.java

public class ModalBox extends DialogFragment{

   ....
   public interface EditProfileModalBoxInterface {
     void onFinishEditProfile( HashMap<String, String> dataPassing );
   }
   ...
   ...
}

2) Inside DialogFragment also i have .setPositiveButton function for OK button. - ModalBox.java

public class ModalBox extends DialogFragment{
   ...       
   ...
   public Dialog onCreateDialog(Bundle savedInstanceState ) {
      ...
      builder
        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(final DialogInterface dialog, int id) {
               // At here i'm using retrofit2 http library
               // to do updating stuff
               // and inside success's callback of retrofit2(asynchronous)
               // here i call the below function to send data 
               // dataToSend is a HashMap value
               sendBackResultToParent( dataTosend );
            }
        })
         .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                // User cancelled the dialog
            }
        });
      .....
   }

   // Function called inside success's callback of retrofit2
   public void sendBackResultToParent( HashMap<String, String> data ) {
      // instantiated interface
      EditProfileModalBoxInterface ls=(EditProfileModalBoxInterface)getTargetFragment();
      // declaring interface's method
      ls.onFinishEditProfile( data );
   }
}

3) Finally, i'm implements those interface inside fragment (2) - ProfileTeacherActivity.java

public class ProfileTeacherActivity extends Fragment 
       implements ModalBox.EditProfileModalBoxInterface{

    public View onCreateView(LayoutInflater inflater, 
                   ViewGroup container, Bundle savedInstanceState ) {
       .....
       .....
    }

    // At here the interface's method did't triggered
    @Override
    public void onFinishEditProfile( HashMap dataPassedFromDialog ) {
      Toast.makeText(getActivity(), "Testing...." , Toast.LENGTH_LONG).show();
    }

}

What i'm confuses right now is, the problem happens only when i called this function sendBackResultToParent( dataTosend ); inside retrofit2 success's callback, it does triggered when calling outside of it. I'm assumed the async called caused this. If i could use Promise or something like that or is there any workaround on this?

The following existing solutions didn't work in my case :

Ask me for more inputs if above use case didn't clear enough or misunderstanding. Thanks for the helps. Regards.

Community
  • 1
  • 1
Norlihazmey Ghazali
  • 9,000
  • 1
  • 23
  • 40

4 Answers4

2

This is a sample DialogFragment code used to send message to selected contact. I too required to capture the click event on the DialogFragment and redirect.

Ideally to achieve this , this is what needed to be done

  • Override the positive/negative button clicks of AlertDialog.Builder and do no action
  • After this , using getButton method mention AlertDialog.BUTTON_NEGATIVE or AlertDialog.BUTTON_POSITIVE and assign an action

public class SMSDialogFrag extends DialogFragment {

    private static String one="one";
    private EditText messageContent;
    private AlertDialog dialog;
    private String mobNumber;

    public static SMSDialogFrag showDialog(String mobNumber){
        SMSDialogFrag customDialogFrag=new SMSDialogFrag();
        Bundle bundle=new Bundle();
        bundle.putString(one, mobNumber);
        customDialogFrag.setArguments(bundle);
        return customDialogFrag;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
        View view = getActivity().getLayoutInflater().inflate(R.layout.sms_dialog, null);

        alertDialogBuilder.setView(view);
        setupUI(view);
        alertDialogBuilder.setTitle("");

        alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                //Do nothing here because we override this button later
            }
        });
        alertDialogBuilder.setPositiveButton("Send", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                //Do nothing here because we override this button later
            }
        });
        dialog = alertDialogBuilder.create();
        dialog.show();
        dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
                //else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
            }
        });
        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendMessage();//IN YOUR USE CASE YOU CAN REDIRECT TO YOUR CALLER FRAGMENT

            }
        });

        return dialog;
    }
    void setupUI(View view){
        TextView textViewMob=(TextView)view.findViewById(R.id.mobNumber);
        messageContent=(EditText)view.findViewById(R.id.messageContent);
        mobNumber=getArguments().getString(one);
        textViewMob.setText("Send message to : "+mobNumber);
    }
    void sendMessage(){
        if( ! TextUtils.isEmpty(messageContent.getText())){
            try {
                SmsManager smsManager = SmsManager.getDefault();
                Log.v(Constants.UI_LOG,"Number >>>>>>> "+mobNumber);
                smsManager.sendTextMessage(mobNumber, null, messageContent.getText().toString(), null, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
            Toast.makeText(getActivity(),"Message Sent!",Toast.LENGTH_SHORT).show();
            dialog.dismiss();
        }else{
            Toast.makeText(getActivity(),"Please enter message to send!",Toast.LENGTH_SHORT).show();
        }
    }
}
Sreehari
  • 5,621
  • 2
  • 25
  • 59
1

Consider using eventBus, for example Otto

The usage is very simple. All you need to do is create an evenbus:

public static Bus bus = new Bus(ThreadEnforcer.MAIN);  //use Dagger2 to avoid static

Then create a receiver method (in fragment 2 in your case):

@Subscribe
public void getMessage(String s) {
  Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}

Send you message by calling(from DialigFramgent):

bus.post("Hello");

And don't forget to register your eventBus inside onCreate method(of your Fragment):

bus.register(this);

And that is!

nutella_eater
  • 3,393
  • 3
  • 27
  • 46
  • If i'm calling `bus.post("Hello");` inside success callback, subscriber's event never trigger. It does trigger, when i'm calling outside success's callback. – Norlihazmey Ghazali Jun 28 '16 at 14:04
1

From architectural standpoint, 2 fragments should not directly communicate with one another. Container Activity should be responsible for passing data between it's child fragments. So here's how i would do it:

Implement your interface in the container Activity and just attach your interface implementation in the Activity to the Dialog class and call that interface method when required. Something like this :

    public static class ModalBox extends DialogFragment {

    EditProfileModalBoxInterface mListener;

    // Container Activity must implement this interface
    public interface EditProfileModalBoxInterface {
     void onFinishEditProfile( HashMap<String, String> dataPassing );
   }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (EditProfileModalBoxInterface) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement EditProfileModalBoxInterface");
        }
    }
}

Then call mListener.onFinishEditProfile(....) where ever it's required in the DialogFragment class.

This way you will receive the result back in your Activity class from where you can call your desired fragment's relevant method to pass the results to that fragment.

This whole flow has been described here

Talha Mir
  • 1,228
  • 11
  • 19
  • Looks like i'm using the wrong concept right now by having two fragments communicating with each others. So, even i'm using `eventBus` like the other guy's is answer, does it not valid or not recommended? – Norlihazmey Ghazali Jun 28 '16 at 07:32
  • @NorlihazmeyGhazali Otto will work too. I use it all the time. It's a handy library. Just wanted to explain how google officially recommends it. But using EventBus is completely valid solution to this issue too. – Talha Mir Jun 28 '16 at 07:36
  • Thanks for clarification. I'm communicate between two fragments directly because want to avoid messing around with main activity, cause i have another fragments to deal with. Anyway, this way does work if i'm calling the `mListener.onFinishEditProfile(....)` outside async success callback of retrofit, unfortunately didn't work when calling inside of it. So weird. – Norlihazmey Ghazali Jun 28 '16 at 07:43
0

Finally the culprit founds. All the answers mentioned above were right. And my script also actually works, the problem is related with my API json's response that did't coming with right structure. In my case, i'm using retrofit2 with GSON converter for parsing into POJO. Found the info on log saying about :

Expected BEGIN_OBJECT but was BEGIN_ARRAY

Means that, GSON expecting the json object, which is my API was returned JSON array. Just change the API's structure then all goods to go. Thanks guys for your hard time. Really appreciated, as i'm right now knowing how to deal with eventBus, replacing default OK and Cancel button and the correct way for communicating between fragments.

Norlihazmey Ghazali
  • 9,000
  • 1
  • 23
  • 40