DialogFragment and onDismiss - android

I am using a DialogFragment, which I am showing like this from an Activity:
DialogFragmentImage dialog = DialogFragmentImage.newInstance(createBitmap());
dialog.onDismiss(dialog);.onDismiss(this);
dialog.show(getFragmentManager(), "DialogFragmentImage");
I would like to check when the DialogFragment was dismissed (for example when the back button was pressed), but in my Activity. How can I do that? How can I "tell" my activity that the DialogFragment has been dismissed?

Make your Activity implement OnDismissListener
public final class YourActivity extends Activity implements DialogInterface.OnDismissListener {
#Override
public void onDismiss(final DialogInterface dialog) {
//Fragment dialog had been dismissed
}
}
DialogFragment already implements OnDismissListener, just override the method and call the Activity.
public final class DialogFragmentImage extends DialogFragment {
///blah blah
#Override
public void onDismiss(final DialogInterface dialog) {
super.onDismiss(dialog);
final Activity activity = getActivity();
if (activity instanceof DialogInterface.OnDismissListener) {
((DialogInterface.OnDismissListener) activity).onDismiss(dialog);
}
}
}
If you're starting the dialog from a fragment using the childFragment manager (API>=17), you can use getParentFragment to talk to the onDismissListener on the parent fragment.:
public final class DialogFragmentImage extends DialogFragment {
///blah blah
#Override
public void onDismiss(final DialogInterface dialog) {
super.onDismiss(dialog);
Fragment parentFragment = getParentFragment();
if (parentFragment instanceof DialogInterface.OnDismissListener) {
((DialogInterface.OnDismissListener) parentFragment).onDismiss(dialog);
}
}
}

Here is my answer. It's a bit late but it's maybe benefit someone passing by.
FragmentManager fm = getFragmentManager();
YourDialogFragment dialog = new YourDialogFragment();
dialog.show(fm,"MyDialog");
fm.executePendingTransactions();
dialog.getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialogInterface) {
//do whatever you want when dialog is dismissed
}
});
We need to call
fm.executePendingTransactions();
To make sure that FragmentTransaction work has been performed. Otherwise NullPointerException can occur when calling setOnDismissListener().
Sorry if there is any mistake. Hope this help.

This is an old issue but I found no solution I am happy with. I don't like passing any Listeners to my DialogFragment or set a TargetFragment, because that may break on orientation change. What do you think about this?
MyDialog d = new MyDialog();
d.show(fragmentManager, "tag");
fragmentManager.registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
super.onFragmentViewDestroyed(fm, f);
//do sth
fragmentManager.unregisterFragmentLifecycleCallbacks(this);
}
}, false);

Alternative answer, if you don't have access to the methode onDismiss of activity.
//DIALOGFRAGMENT
//Create interface in your DialogFragment (or a new file)
public interface OnDismissListener {
void onDismiss(MyDialogFragment myDialogFragment);
}
//create Pointer and setter to it
private OnDismissListener onDismissListener;
public void setDissmissListener(DissmissListener dissmissListener) {
this.dissmissListener = dissmissListener;
}
//Call it on the dialogFragment onDissmiss
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (onDismissListener != null) {
onDismissListener.onDismiss(this);
}
}
//OTHER CLASS, start fragment where you want
MyDialogFragment df = new MyDialogFragment();
df.setOnDismissListener(new MyDialogFragment.OnDismissListener() {
#Override
public void onDismiss(MyDialogFragment myDialogFragment) {
//Call when MyDialogFragment close
}
});
df.show(activity.getFragmentManager(), "myDialogFragment");
edit : if system need to recreate DialogFragment:
you can find it with
MyDialogFragment myDialogFragment = getFragmentManager().findFragmentByTag("MyDialogFragment");
if(myDialogFragment != null) {
myDialogFragment.setOnDismissListener(...);
}

public class OpcoesProdutoDialogo extends DialogFragment{
private DialogInterface.OnDismissListener onDismissOuvinte;
.
.
.
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if(onDismissOuvinte!=null)
onDismissOuvinte.onDismiss(dialog);
}
public void setOnDismissListener(#Nullable DialogInterface.OnDismissListener listener) {
this.onDismissOuvinte = listener;
}
}
and in call
OpcoesProdutoDialogo opcProduto = OpcoesProdutoDialogo.criar(itemPedido);
opcProduto.show(getFragmentManager(), "opc_produto_editar");
opcProduto.setOnDismissListener(d->{
adapterItens.notifyItemChanged(posicao);
});

You can subclass DialogFragment and provide your own listener that is going to be called and in onCancel.
var onDismissListener: (() -> Unit)? = null
For the ones not familiar with Kotlin this is just an anonymous interface that saves boilerplate iterface in Java. Use a field and a setter in Java.
And then in onCancel
override fun onCancel(dialog: DialogInterface?) {
super.onCancel(dialog)
onDismissListener?.invoke()
}
Have fun!

If you don't like the solution of #yaroslav-mytkalyk, in which the fragment needs to cast the activity / parent fragment, here's another one:
Here's the idea:
Expose a listener in your fragment, DialogFragmentImage.
Implement the listener in your activity and pass it to the fragment when creating it. Make sure to use a tag as well in order to be able to find the fragment later (read below).
In onStop(), remove the listener in order not to leak the activity if it's destroyed. This will happen when the screen is rotated, as the activity will be re-created.
In onResume(), check if the fragment exists and if yes, re-add the listener.
Expose a listener from your fragment:
class MyFragment extends DialogFragment {
public interface OnDismissListener {
void dismissed();
}
#Nullable
private OnDismissListener onDismissListener;
public void setOnDismissListener(#Nullable OnDismissListener onDismissListener) {
this.onDismissListener = onDismissListener;
}
/*
If you are calling dismiss() or dismissAllowingStateLoss() manually,
don't forget to call:
if (onDismissListener != null) {
onDismissListener.dismissed();
}
Otherwise, override them and call it there.
*/
}
And this is how your activity should look like:
class MyActivity extends AppCompatActivity {
private static final String MY_FRAGMENT_TAG = "my_fragment";
private MyFragment.OnDismissListener myFragmentListener = () -> {
// ...
};
/**
* Shows the fragment. Note that:
* 1. We pass a tag to `show()`.
* 2. We set the listener on the fragment.
*/
private void showFragment() {
MyFragment fragment = new MyFragment();
fragment.show(getSupportFragmentManager(), MY_FRAGMENT_TAG);
fragment.setOnDismissListener(myFragmentListener);
}
#Override
protected void onStart() {
super.onStart();
// Restore the listener that we may have removed in `onStop()`.
#Nullable MyFragment myFragment = (MyFragment) getSupportFragmentManager().findFragmentByTag(MY_FRAGMENT_TAG);
if (myFragment != null) {
myFragment.setOnDismissListener(myFragmentListener);
}
}
#Override
protected void onStop() {
// If the fragment is currently shown, remove the listener so that the activity is not leaked when e.g. the screen is rotated and it's re-created.
#Nullable MyFragment myFragment = (MyFragment) getSupportFragmentManager().findFragmentByTag(MY_FRAGMENT_TAG);
if (myFragment != null) {
myFragment.setOnDismissListener(null);
}
super.onStop();
}
}

Care : all example aren't correct because your fragment should have a no-arg constructor !
Working code with back gesture and close button in the fragment itself. I removed useless code stuff like getting arg in onCreate etc.
Important : onDismiss is also call when orientation change so as a result you should check if the context is not null in your callback (or using other stuff).
public class MyDialogFragment extends DialogFragment {
public static String TAG = "MyFragment";
public interface ConfirmDialogCompliant {
void doOkConfirmClick();
}
public MyFragment(){
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_layout, container, false);
((ImageButton) rootView.findViewById(R.id.btn_close)).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// close fragment
dismiss();
}
});
return rootView;
}
#Override
public void onDismiss(#NonNull DialogInterface dialog) {
super.onDismiss(dialog);
// notify
if(caller != null)
caller.doOkConfirmClick();
}
}
public void setCallback(ConfirmDialogCompliant caller) {
this.caller = caller;
}
public static MyDialogFragment newInstance(String id) {
MyDialogFragment f = new MyDialogFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putString("YOU_KEY", id);
f.setArguments(args);
return f;
}
}
And now how to call it from parent.
MyDialogFragment.ConfirmDialogCompliant callback = new MyDialogFragment.ConfirmDialogCompliant() {
#Override
public void doOkConfirmClick() {
// context can be null, avoid NPE
if(getContext() != null){
}
}
};
MyDialogFragment fragment = MyDialogFragment.newInstance("item");
fragment.setCallback(callback);
fragment.show(ft, MyDialogFragment.TAG);
new MyDialogFragment(callback, item);
fragment.show(getActivity().getSupportFragmentManager(), MyDialogFragment.TAG);
Additionnal source : https://developer.android.com/reference/android/app/DialogFragment

Kotlin Answer
private fun showMyCustomDialog() {
// Show.
MyCustomDialogFragment().show(fm, "MyCustomDialogFragment")
// Set pending transactions.
fm.executePendingTransactions()
// Listen dialog closing.
MyCustomDialogFragment().dialog?.setOnDismissListener {
// You can do you job when it closed.
}
}

Solution using kotlin and additional interface. (an example for a fragment will be shown here, but with a few changes it will work in an activity as well)
First you need to create an interface (the set of parameters can be any):
interface DialogCloseListener {
fun handleDialogClose(dialog: DialogInterface)
}
Then implement this interface in the fragment that calls the DailogFragment:
class YourParentFragment: Fragment(), DialogCloseListener {
override fun handleDialogClose(dialog: DialogInterface) {
// do something
}
}
Now go to your DialogFragment. Implement the onDismiss method. In it, check if the parent fragment implements your interface, call your method, passing the necessary parameters there:
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
if(parentFragment is DialogCloseListener){
(parentFragment as DialogCloseListener).handleDialogClose(dialog)
}
}
I think that this way is good because you can track a specific close event (by passing a certain parameter to the method), for example, canceling an order, and somehow handle it.

Try this
dialog.setOnDismissListener {
Log.e("example","example")
}
Have Fun!

Related

How to add onBackPressedCallback to fragment?

I've seen this mentioned in a couple of places but cant quite work out how to implement it as there are no example online, I'm wondering whether I've misinterpreted its meaning.
the android developer link for addOnBackPressedCallback
I was wondering whether this means I can add a callback inside my fragment to pick up the onBackPressed() before it hits the activity.
In my fragment I've implemented
implements OnBackPressedCallBack
then implemented the method
#Override
public boolean handleOnBackPressed() {
Log.d(TAG, "handleOnBackPressed: hello");
return true;
}
But its not picking it up and going straight to main activity onBackPressed().
I read on further and saw that My fragment needed to extend from FragmentActivity
public class MainExampleFragment extends FragmentActivity implements View.OnClickListener, OnBackPressedCallback {
and instead of
appCompatActivity.getSuppportFragmentManager
to call
FragmentActivity.getSupportFragmentManager
instead, but this throws and error in the Fragment.newInstance method when trying to set the bundle.
Has anyone info on doing this please thanks
As #Derek Zhu pointed out, it seems that if you override onBackPressed in your activity then the Fragment's onBackPressedCallback doesn't work. I found that if I wanted to also use it in the activity then I needed to use onBackPressedDispatcher.addCallback there also.
Activity:
onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Log.d(TAG, "Activity back pressed invoked")
// Do custom work here
// if you want onBackPressed() to be called as normal afterwards
if (isEnabled) {
isEnabled = false
requireActivity().onBackPressed()
}
}
}
)
Fragment:
requireActivity().onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Log.d(TAG, "Fragment back pressed invoked")
// Do custom work here
// if you want onBackPressed() to be called as normal afterwards
if (isEnabled) {
isEnabled = false
requireActivity().onBackPressed()
}
}
}
)
The result of the above is that Fragment back pressed will be invoked, then Activity back pressed.
This is what I did
public class MyFragment extends Fragment {
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
}
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true /* Enabled by default */) {
#Override
public void handleOnBackPressed() {
// Handle the back button event
if (fieldsModified()) {
// User made changes on EditText on the Fragment. Confirm exit to avoid loss of data
AlertDialog.Builder builder= new AlertDialog.Builder(getContext());
builder.setMessage(msg_confirm_close_form);
builder.setPositiveButton(R.string.btn_yes, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
closeFragment();
}
});
builder.setNegativeButton(R.string.btn_no, null);
builder.show();
}
else {
closeFragment();
}
}
private void closeFragment() {
// Disable to close fragment
this.setEnabled(false);
requireActivity().getOnBackPressedDispatcher().onBackPressed();
}
};
}
You must implement a callback in your Fragment to intercept the onBackPressed event throught a dispatcher from you activity.
if anybody dont know i am update OnBackPressedCallBack for both fragment and AppCompacTActivity and I have Nested Fragment here
in Fragment
public class MessageFragment extends Fragment {
public MessageFragment() {
// Required empty public constructor
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
// This callback will only be called when MyFragment is at least Started.
OnBackPressedCallback callback = new OnBackPressedCallback(true ) {
#Override
public void handleOnBackPressed() {
// Handle the back button event
FragmentManager fm= getFragmentManager();
if (fm != null) {
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
Log.e( "backpress Frag","back" );
}
List<Fragment> fragList = fm.getFragments();
if (fragList != null && fragList.size() > 0) {
for (Fragment frag : fragList) {
if (frag == null) {
continue;
}
if (frag.isVisible()) {
Log.e( "backpress Frag","Visible" );
}
}
}
}
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
super.onCreate( savedInstanceState );
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root=inflater.inflate( R.layout.fragment_listning, container, false );
return root;
}
and you in MainActivity remove your OnBackpressed() method and add this new Method
OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback( true ) {
boolean doubleBackToExitPressedOnce=false;
#Override
public void handleOnBackPressed() {
if (doubleBackToExitPressedOnce) {
ActivityCompat.finishAffinity( FirstActivity.this );
Log.e( "Click", "double back" );
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(FirstActivity.this,getStrin(R.string.exitapp),Toast.LENGTH_SHORT).show();
new Handler().postDelayed( new Runnable() {
#Override
public void run() {
doubleBackToExitPressedOnce=false;
}
}, 2000);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_first );
//getting bottom navigation view and attaching the listener
getOnBackPressedDispatcher().addCallback( this, onBackPressedCallback );
}
AppCompatActivity extends FragmentActivity extends
ComponentActivity(androidx.fragment.app.ComponentActivity) extends
(androidx.core.app.ComponentActivity)
so addOnBackPressedCallback should work for AppCompatActivity too
Updated Approach
It is now very easy to add backpress handler to any fragment with
https://developer.android.com/reference/androidx/activity/OnBackPressedDispatcher
Deprecated Approach: DO NOT USE
You can't directly handle onBackpressed inside a fragment.
What you can do instead is pass the onBackPressed event to the current displayed fragment through some way.
One basic way is to create an interface like so:
interface BackHandler {
handleBackPressed(): Boolean
}
then implementing this on your fragment like so:
class YourFragment : Fragment(), BackHandler {
...
override handleBackPressed(): Boolean {
// do Your thing here
}
...
}
On your Activity do this on onBackPressed:
class YourActivity: Activity() {
...
override onBackPressed() {
val currentFragment = supportFragmentManager.findFragmentById(yourviewid)
if (currentFragment is BackHandler) {
(currentFragment as BackHandler).handleBackPressed()
} else {
super.onBackPressed()
}
}
...
}
That is just one way to do it. Yo could also use something like an EventBus or some other implementation. The key point is that Android doesn't provide this out of the box for fragments.
Kotlin:
val mOnBackPressedCallback= object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (active) {
// Do something
} else {
// call onBackPressed() as normal afterwards
remove()
requireActivity().onBackPressed()
}
}
}
Add Callback to dispatcher:
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, mOnBackPressedCallback)

How to implement an interface in Fragment from a non parent Activity?

I have not found a clear solution anywhere on stack for this.
Here's my basic set up
public class Activity1 extends AppCompatActivity
{
private OnAttributesUpdatedListener onAttributesUpdatedListener;
public interface OnAttributesUpdatedListener
{
public void onAttributesUpdated();
}
public void setTargetFragment(Fragment fragment)
{
this.onAttributesUpdatedListener = (OnAttributesUpdatedListener) fragment;
}
private void whenFinishedSomethingCallback()
{
onAttributesUpdatedListener.onAttributesUpdated();
}
}
public class Fragment1 extends Fragment implements Activity1.OnAttributesUpdatedListener
{
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view) {
if(rivalButtonClick == 0)
{
Activity1 activity1 = new Activity1();
activity1.setTargetFragment(Fragment1.this);
startActivity(new Intent(getActivity(), activity1.getClass()));
}
}
});
}
I get a null pointer exception and crashes on : onAttributesUpdatedListener.onAttributesUpdated(); because for some reason my listener never gets set properly. What's the proper way to do this?
You need to set the listener at start of the fragment onCreatView() or in onActivityCreated() only if the Desired Activity is a parent Activity of that particular fragment. Below is an example .
public class Activity1 extends AppCompatActivity {
private OnAttributesUpdatedListener onAttributesUpdatedListener;
public interface OnAttributesUpdatedListener {
public void onAttributesUpdated();
}
public void setListener(OnAttributesUpdatedListener onAttributesUpdatedListener) {
this.onAttributesUpdatedListener = onAttributesUpdatedListener;
}
private void whenFinishedSomethingCallback() {
if(onAttributesUpdatedListener!=null)
onAttributesUpdatedListener.onAttributesUpdated();
}
}
public class Fragment1 extends Fragment implements Activity1.OnAttributesUpdatedListener
{
#Override
public void onAttributesUpdated() {
// Do your stuff here
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((Activity1)getActivity()).setListener(this);
}
}
Read about fragment Life cycle to make use of getActivity(). also remove the listener when fragment is destroyed .
Use LocalBroadcastManager for communicating between in case the Fragment exists in other Activity.
At first create an Interface like this:
public interface Listener{
void doSomething() }
Then implement this interface in your activity.
And also add
Listener listener
In your fragment
And in onAttach method in fragment use this
listener=(Listener)activity
Then call listener whenever you need .

How do you implement callbacks in Android/Java? [duplicate]

I have this interface in my activity.
public interface LogoutUser {
void logout();
}
My fragment implements this interface, so in my fragment, I have this:
#Override
public void logout() {
// logout
}
In my activity I call
mLogoutUser.logout();
Where mLogoutUser is of the type LogoutUser interface.
My issue is the mLogoutUser object that is null. How can initialize it?
Thank you!
As I said in my comment, I resolved this issue using onAttach method in my fragment, but in this way you have to have the callback field (mLogoutUser in this case) declared in the fragment, and initialize it this way:
public class MyFragment extends ListFragment {
LogoutUser mLogoutUser;
// Container Activity must implement this interface
public interface LogoutUser {
public void logout();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mLogoutUser = (LogoutUser) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement LogoutUser");
}
}
...
}
More info in Communicating with Other Fragments.
But if your case is the field declared in the activity, you can use the onAttachFragment method from your activity to initialize your listener field this way:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
mLogoutUser = (LogoutUser) fragment;
}
Also, you can use an event bus to make this communication between fragments and activities. An option is the Otto library, from Square.
Sample for creating callback from Fragment to Activity
public interface CallBackListener {
void onCallBack();// pass any parameter in your onCallBack which you want to return
}
CallBackFragment.class
public class CallBackFragment extends Fragment {
private CallBackListener callBackListener;
public CallBackFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_call_back, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//getActivity() is fully created in onActivityCreated and instanceOf differentiate it between different Activities
if (getActivity() instanceof CallBackListener)
callBackListener = (CallBackListener) getActivity();
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button btn = (Button) view.findViewById(R.id.btn_click);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(callBackListener != null)
callBackListener.onCallBack();
}
});
}
}
CallbackHandlingActivity.class
public class CallbackHandlingActivity extends AppCompatActivity implements CallBackListener
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_all_user);
}
#Override
public void onCallBack() {
Toast.makeText(mContext,"onCallback Called",Toast.LENGTH_LONG).show();
}
}
Android Fragments - Communicating with Activity
You need to get a reference to your fragment with getFragmentById() or getFragmentByTag()
getFragmentManager().findFragmentById(R.id.example_fragment);
You can use kotlinx Channel to send data or callback between fragments and activity or vice versa
In your Mainactivity:
val loginPromptChannel = Channel<LoginPromptState>()
val loginStateFlow = loginPromptChannel.receiveAsFlow()
//onCreate
lifecycleScope.launchWhenStarted {
loginStateFlow.collect() { state ->
when (state) {
is LoginPromptState.Login -> {
//smooth scroll to login fragment
binding.viewpager.setCurrentItem(2, true)
}
}
}
}
//create sealed a class
sealed class LoginPromptState {
object Login : LoginPromptState()
}
In your fragment send callback like:
lifecycleScope.launch {
val channelLogin = (activity as MainActivity).loginPromptChannel
channelLogin.send(MainActivity.LoginPromptState.Login)
}

How to make a callback between Activity and Fragment?

I have this interface in my activity.
public interface LogoutUser {
void logout();
}
My fragment implements this interface, so in my fragment, I have this:
#Override
public void logout() {
// logout
}
In my activity I call
mLogoutUser.logout();
Where mLogoutUser is of the type LogoutUser interface.
My issue is the mLogoutUser object that is null. How can initialize it?
Thank you!
As I said in my comment, I resolved this issue using onAttach method in my fragment, but in this way you have to have the callback field (mLogoutUser in this case) declared in the fragment, and initialize it this way:
public class MyFragment extends ListFragment {
LogoutUser mLogoutUser;
// Container Activity must implement this interface
public interface LogoutUser {
public void logout();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mLogoutUser = (LogoutUser) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement LogoutUser");
}
}
...
}
More info in Communicating with Other Fragments.
But if your case is the field declared in the activity, you can use the onAttachFragment method from your activity to initialize your listener field this way:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
mLogoutUser = (LogoutUser) fragment;
}
Also, you can use an event bus to make this communication between fragments and activities. An option is the Otto library, from Square.
Sample for creating callback from Fragment to Activity
public interface CallBackListener {
void onCallBack();// pass any parameter in your onCallBack which you want to return
}
CallBackFragment.class
public class CallBackFragment extends Fragment {
private CallBackListener callBackListener;
public CallBackFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_call_back, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//getActivity() is fully created in onActivityCreated and instanceOf differentiate it between different Activities
if (getActivity() instanceof CallBackListener)
callBackListener = (CallBackListener) getActivity();
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button btn = (Button) view.findViewById(R.id.btn_click);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(callBackListener != null)
callBackListener.onCallBack();
}
});
}
}
CallbackHandlingActivity.class
public class CallbackHandlingActivity extends AppCompatActivity implements CallBackListener
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_all_user);
}
#Override
public void onCallBack() {
Toast.makeText(mContext,"onCallback Called",Toast.LENGTH_LONG).show();
}
}
Android Fragments - Communicating with Activity
You need to get a reference to your fragment with getFragmentById() or getFragmentByTag()
getFragmentManager().findFragmentById(R.id.example_fragment);
You can use kotlinx Channel to send data or callback between fragments and activity or vice versa
In your Mainactivity:
val loginPromptChannel = Channel<LoginPromptState>()
val loginStateFlow = loginPromptChannel.receiveAsFlow()
//onCreate
lifecycleScope.launchWhenStarted {
loginStateFlow.collect() { state ->
when (state) {
is LoginPromptState.Login -> {
//smooth scroll to login fragment
binding.viewpager.setCurrentItem(2, true)
}
}
}
}
//create sealed a class
sealed class LoginPromptState {
object Login : LoginPromptState()
}
In your fragment send callback like:
lifecycleScope.launch {
val channelLogin = (activity as MainActivity).loginPromptChannel
channelLogin.send(MainActivity.LoginPromptState.Login)
}

onAttach() never called in DialogFragment

I try to implement a callback from a DialogFragment.
There is a good example, but they don't open this DialogFragment from a Fragment.
http://developer.android.com/guide/topics/ui/dialogs.html#PassingEvents
So here is my code:
public class EditDateDialogFragment extends DialogFragment {
// Use this instance of the interface to deliver action events
EditDateDialogListener mListener;
/* The activity that creates an instance of this dialog fragment must
* implement this interface in order to receive event callbacks.
* Each method passes the DialogFragment in case the host needs to query it. */
public interface EditDateDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
public static EditDateDialogFragment newInstance( int currentCategoryId ) {
EditDateDialogFragment p = new EditDateDialogFragment();
Bundle args = new Bundle();
args.putInt("currentRecordId", currentCategoryId);
p.setArguments(args);
return p;
}
#Override
public void onCreate(Bundle savedInstanceState) {
mCurrentRecordId = getArguments().getInt("currentRecordId");
super.onCreate(savedInstanceState);
}
public void onAttach(SherlockActivity activity) {
super.onAttach(activity);
try {
// Instantiate the EditDateDialogListener so we can send events to the host
mListener = (EditDateDialogListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString() + " must implement EditDateDialogListener");
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = LayoutInflater.from(getActivity());
final View v = inflater.inflate(R.layout.fragment_dialog_edit_date, null);
return new AlertDialog.Builder(getActivity()).setTitle("Set Date...").setView(v).setCancelable(true).setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.d("", "Dialog confirmed");
mListener.onDialogPositiveClick(EditDateDialogFragment.this);
}
}).setNegativeButton("Abort", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.d("", "Dialog abort");
dialog.cancel();
}
}).create();
}
}
In RecordDetailFragment.java i implement the interface and create a new instance of the EditDateDialogFragment at this way (just the important parts):
public class RecordDetailFragment extends SherlockFragment implements EditDateDialogFragment.EditDateDialogListener {
...
DialogFragment editDateFragment = EditDateDialogFragment.newInstance( recordId );
editDateFragment.show(getActivity().getSupportFragmentManager(), "EditDateDialogFrame");
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
LOGD(TAG, "Overriden Dialog confirmed");
//((EditDateDialogFragment) dialog).mDatePicker;
}
#Override
public void onDialogNegativeClick(DialogFragment dialog) {
// TODO Auto-generated method stub
}
...
}
Now the public void onAttach(SherlockActivity activity) in the EditDateDialogFragment is never called, because I create the DialogFragment from a Fragment instead of an Activity?
How to fix this?
UPDATE:
In the RecordDetailFragment I insert this into the onCreate()
if (savedInstanceState != null) {
EditDateDialogFragment dpf = (EditDateDialogFragment) getActivity().getSupportFragmentManager().findFragmentByTag("EditDateDialogFragment");
if (dpf != null) {
dpf.setListener((EditDateDialogListener) this);
}
}
I changed the instantiation of the DialogFragment to
EditDateDialogFragment editDateFragment = EditDateDialogFragment.newInstance( recordId );
editDateFragment.setListener((EditDateDialogListener) this);
editDateFragment.show(getActivity().getSupportFragmentManager(), "EditDateDialogFragment");
Note the EditDateDialogFragment instead of DialogFragment.
I'm not sure how to update the reference in the dialog.
Just jumped into the same problem, the solution was very simple. Instead of overriding
public void onAttach(Context context) {}
override this:
public void onAttach(Activity activity) {}
Everything is now fine with DialogFragment.
How to fix this?
I'm guessing that you want the RecordDetailFragment instance to behave as the EditDateDialogListener for the DialogFragment. If yes then you need to explicitly set it(and update it) as the listener:
DialogFragment editDateFragment = EditDateDialogFragment.newInstance( recordId );
editDataFragment.setListener(RecordDetailFragment.this);
editDateFragment.show(getActivity().getSupportFragmentManager(), "EditDateDialogFrame");
Where setListener() is a method in the EditDialogFragment like this:
public void setListener(EditDateDialogListener listener) {
mListener = listener;
}
As the user rotates the phone, for example, the Activity along with its fragments will be recreated and you need to re set the listener to point to the newly created RecordDetailFragment instance(you may want to use a WeakReference for mListener). Something similar you can find in this answer(you'll look for the two fragments in the onCreate).
Edit: In the onCreate method of the Activity:
if (savedInstanceState != null) {
RecordDetailFragment df = (RecordDetailFragment) getSupportFragmentManager().findFragmentByTag("rdf"); // "rdf" is the tag used when you add the RecordDetailFragment to the activity
EditDateDialogFragment s = (EditDateDialogFragment) getSupportFragmentManager().findFragmentByTag("tag"); // "tag" is the string set as the tag for the dialog when you show it
if (s != null) {
// the dialog exists so update its listener
s.setListener(df);
}
}
Somewhere in the onCreateDialog cast the mListener to the getActivity():
try {
mListener = (EditDateDialogListener) getActivity();
} catch (Exception e) {
throw new ClassCastException(getActivity().toString()
+ " must implement EditDateDialogListener");
}
A more "modern" approach is to use the new Fragment Result API.
Add a result listener on Fragment A (parent) onCreate:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
childFragmentManager.setFragmentResultListener("requestKey", this) { key, bundle ->
val result = bundle.getString("bundleKey")
}
}
Wherever you need, set result on child Fragment B (on a button click listener, for instance):
button.setOnClickListener {
val result = "resultSample"
setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}
More info on the docs: https://developer.android.com/guide/fragments/communicate#kotlin

Categories

Resources