I'd like to show an AlertDialog (with a couple of Radio Button buttons and an OK button) as soon as a fragment is created.
Where is the best place to call the dialog fragment? I have tried in onViewCreated() and on onResume() and both work, but I am not sure what's best practice.
Also, to ensure the dialog isn't shown every time the fragment is stopped/recreated due, for example, to screen rotation, I have created Boolean value called mShowDialog and set it to 'true' in onCreate() then used an 'If' statement to decide whether the dialog should be shown (see below for example).
onCreate(){
//....
mShowDialog = true;
}
onResume(){
if (mShowDialog){
//....show dialog code
// set mShowDialog to false to ensure code executed only once
mShowDialog = false;
}
}
Is the above code the best way of fulfilling both requirements?
Btw, I am fairly new to programming.
Thanks in advance for the help.
Best practice for this is to inflate dialog in onCreateView() method of fragment.
If you're trying to create it from the activity adding the fragment, I've had good luck with adding a FragmentListener to my fragments and setting it from the activity. This is my basic BaseFragment class that all my fragments extend:
public class BaseFragment extends Fragment {
public Context context;
public Activity activity;
public FragmentListener fragmentListener;
private boolean attached = false;
public BaseFragment() {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!isAttached()) {
this.context = activity;
this.activity = activity;
if (this.fragmentListener != null) {
this.fragmentListener.onAttached();
}
setAttached(true);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (!isAttached()) {
this.context = context;
this.activity = (Activity) context;
if (this.fragmentListener != null) {
this.fragmentListener.onAttached();
}
setAttached(true);
}
}
#Override
public void onDetach() {
super.onDetach();
setAttached(false);
if (this.fragmentListener != null){
this.fragmentListener.onDetached();
}
}
public void setFragmentListener(FragmentListener fragmentListener) {
this.fragmentListener = fragmentListener;
}
public View.OnClickListener onBackTapped = new View.OnClickListener() {
#Override
public void onClick(View v) {
getActivity().onBackPressed();
}
};
public boolean isAttached() {
return attached;
}
public void setAttached(boolean attached) {
this.attached = attached;
}
public boolean isPermissionGranted(String permission){
return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
}
public boolean ifShouldShowRationaleForPermission(String permission){
return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
}
public void showPermissionRequest(Activity activity, int requestCode, String... permissions){
ActivityCompat.requestPermissions(activity, permissions, requestCode);
}
}
This way, I can do this in my activity:
MyFragment myFragment = new MyFragment();
myfragment.setFragmentListener(new FragmentListener() {
#Override
public void onAttached() {
// Stuff I want to do when it is attached
}
#Override
public void onDetached() {
// Stuff I want to do when it is detached
}
});
fragmentManager.beginTransaction()
.add(R.id.container, myFragment)
.commit();
And then I can add whatever code I want when the fragment does it's various stuff.
Good luck!
Related
I want to realize the navigation of the fragments using the following code:
public abstract class BaseFragment extends Fragment {
private static String TAG = "BaseFragment";
private BaseFragmentActions baseFragmentActions;
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity activity = null;
if (context instanceof Activity){
activity = (Activity) context;
}
Log.i(TAG, "onAttach = ");
try {
baseFragmentActions = (BaseFragmentActions)activity;
} catch (ClassCastException e) {
}
Log.i("onAttach",""+(getBackStackCount()!=0));
baseFragmentActions.resetToolbarNavigation(getBackStackCount()!=0);
}
#Override
public void onDetach() {
super.onDetach();
Log.i("BaseFragment", "onDestroy = " + (getBackStackCount() - 1));
baseFragmentActions.resetToolbarNavigation((getBackStackCount() - 1) != 0);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
private int getBackStackCount() {
int b = getActivity().getSupportFragmentManager().getBackStackEntryCount();
Log.i("getBackStackEntryCount", "====== "+b);
return b;
}
public interface BaseFragmentActions {
public void resetToolbarNavigation(boolean backNavigationEnabled);
}
}
All my fragments extend this Base Activity. And inside my main activity i implement BaseFragmentActions, and implemented this method:
#Override
public void resetToolbarNavigation(boolean backNavigationEnabled) {
Log.i("BaseActivity", "reset " + backNavigationEnabled);
getSupportActionBar().setDisplayHomeAsUpEnabled(backNavigationEnabled);
if (backNavigationEnabled) {
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.i("resetToolbarNavigation", "setNavigationOnClickListener");
onBackPressed();
}
});
} else {
initNavigation();
syncState();
}
}
Everything works fine but when I change the screen orientation we obtain error that getSupportActionBar = null.
This is because of what I call going to attach. How can I fix this error? I tried to make checking whether getSupportActionBar is not zero. I'm not getting an error, but "up" Arrow replaced hamburger...
Advise what you can do in this case. Also share links to navigate the implementation of such fragments. Sorry if something wrong written, or I made a grammatical error)).
Hi sorry for the delay in the answer, the problem you're having is because when onAttach is called the getSupportActionBar() is not set yet, instead you need to make sure the Activity is already created when interacting with Activity components, so just put your call inside the onActivityCreated method of your Fragment like this:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
baseFragmentActions.resetToolbarNavigation(getBackStackCount()!=0);
}
I have been researching this for a few days and have yet to find a working solution. There is lots of information available but because of my inexperience with Android I can't get any of the suggestions to work.
I have an Activity with a stack of 3 Fragments on top of it all of which are presented using FragmentManager Transactions and added to the backstack. While the third Fragment is active, I need to intercept the onBackPressed() method and perform some extra stuff before the Fragment is destroyed.
I have tried using Callbacks and Interfaces to capture onBackPressed() at the Activity and send it to the 3rd Fragment with no luck.
What is the proper way to have a Fragment deep in the stack watch for the Activity's onBackPressed() method.
Let me know if this is not clear.
Thanks for the help.
Not compiled and tested, but this lays out the basic approach:
public interface BackButonListener {
boolean OnBackButtonPressed();
}
public interface BackButtonWatchable {
void addBackButtonListener(BackButtonListener listener);
void removeBackButtonListener(BackButtonListener listener);
}
public class MyActivity extends Activity implements BackButtonWatchable {
...
private static ArrayList<BackButtonListener> backButtonListeners
= new ArrayList<BackButtonListener>();
#Override
public void addBackButtonListener(BackButtonListener listener) {
backButtonListeners.add(listener);
}
#Override
public void removeBackButtonListener(BackButtonListener listener) {
backButtonListeners.remove(listener);
}
...
#Override
public void onBackButtonPressed()
{
boolean supressBackButton = false;
for (BackButtonListener listener: backButtonListeners)
{
if (!listener.OnBackButtonPressed()) {
suppressBackButton = true;
}
}
if (!suppressBackButton) {
super.onBackButtonPressed();
}
}
}
public class MyFragment extends Fragment implements BackButtonListerer {
#Override
public void onResume()
{
((BackButtonWatchable)getActivity()).addBackButtonListener(this);
}
#Override
public void onPause() {
((BackButtonWatchable)getActivity()).removeBackButtonListener(this);
}
}
Crete interface
public interface OnBackPressedListener {
void onBackPressed();
}
and create field in activity
private OnBackPressedListener mListener;
and your onBackPressed() should look like
if (mListener != null) {
mListener.onBackPressed();
} else { /* do your acitivty usual stuff */ }
When fragment is created you register this fragment as mListener in your activity and don't forger to set it to null in onDestroy.
This is the post that answered my question. For a Android newbie, this told me where everything needed to go.
https://stackoverflow.com/a/30865486/2640458
The Fragment that needed to see the onBackPress() method from it's activity:
public class RatingFragment extends Fragment implements ContentActivity.OnBackPressedListener {
#Override
public void doBack() {
getFragmentManager().popBackStack();
}
The very important subscription to the listener in the above Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_rating, container, false);
((ContentActivity)getActivity()).setOnBackPressedListener(this);
}
The Activity that needs to send the onBackPress() method to the above Fragment:
public class ContentActivity extends Activity {
protected OnBackPressedListener onBackPressedListener;
public interface OnBackPressedListener {
void doBack();
}
public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
this.onBackPressedListener = onBackPressedListener;
}
#Override
public void onBackPressed() {
if (onBackPressedListener != null)
onBackPressedListener.doBack();
else
super.onBackPressed();
}
#Override
protected void onDestroy() {
onBackPressedListener = null;
super.onDestroy();
}
}
I've some rare crash reports of a live app, with OnClickListener of a Gridview being called after the destruction of its parent fragment.
Here's a MCVE of the situation:
public class MyFragment extends Fragment
{
private Activity activity;
#Override
public void onAttach(Activity activity) {
super.onAttach();
this.activity = activity;
}
#Override
public void onDetach() {
super.onDetach();
this.activity = null;
}
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
activity.doSomething();
}
}
}
}
activity.doSomething() sometimes crashes with a NullPointerException. There's no other place where I write on this.activity besides onAtach() and onDetach().
I understand that a quick fix would be just checking if the Fragment isAdded() or the activity field for null, but I want to find the reason behind this crash.
Is this a bug, or expected behaviour and I should always be checking the state of the fragment?
As #M D and #shkschneider mentioned above, you should use Interface here
I have tried to provide sample here with code :
public class MyFragment extends Fragment
{
private Activity activity;
private ArrayList<DoSomethingInterface> callback = new ArrayList<DoSomethingInterface>();
private DoSomethingInterface callback1;
public MyFragment(DoSomethingInterface interface) {
this.callback1 = interface;
}
#Override
public void onAttach(Activity activity) {
super.onAttach();
this.activity = activity;
}
#Override
public void onDetach() {
super.onDetach();
this.activity = null;
}
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
//activity.doSomething();
if(callback.size() > 0) {
for(int i = 0; i < callback.size(); i++) {
callback.get(i).doSomething();
}
}
if(callback1 != null) {
callback1.doSomething();
}
}
}
}
public interface DoSomethingInterface {
public void doSomething();
}
public void registerListener(DoSomethingInterface interface) {
//this.callback = interface;
callback.add(interface);
}
}
OtherActivity.java :
public OtherActivity extends Activity implements doSomethingInterface {
private MyFragment myFragmentObject;
public void onCreate() {
// pass reference to MyFragment using constructor
myFragmentObject = new MyFragment(this);
// or other way, using a method
myFragmentObject.registerListener(this);
}
#Override
public void doSomething() {
// callback will come
}
}
Basically you need to remove the listener in onDetach(). The reason is the Fragment may be amid of detaching, and a UI listener may be triggered after detached. That will confuse most apps.
Code suggestion:
#Override
public void onDetach() {
if (gridView != null) {
gridView.setOnItemClickListener( null );
}
super.onDetach();
// this.activity = null;
}
Note:
Call setOnItemClickListener() passing null as parameter. I did not find any documentation to say this but this is a known trick. Besides that, there are some listeners with custom removal methods.
I would remove code this.activity = null because this may be the code that caused the crash/exception. And conceptually it's better that the Activity class set this object instead of the Fragment class.
EDIT: This question was created as part of one of my first Android projects when I was just starting out with Android application development. I'm keeping this for historical reasons, but you should consider using EventBus or RxJava instead. This is a gigantic mess.
Please DO NOT CONSIDER using this. Thank you.
In fact, if you want something cool that solves the problem of using a single activity with multiple "fragments", then use flowless with custom viewgroups.
I have implemented a way to initiate the creation of Fragments, from Fragments using a broadcast intent through the LocalBroadcastManager to tell the Activity what Fragment to instantiate.
I know this is a terribly long amount of code, but I'm not asking for debugging, it works perfectly as I intended - the data is received, the creation can be parametrized by Bundles, and Fragments don't directly instantiate other Fragments.
public abstract class FragmentCreator implements Parcelable
{
public static String fragmentCreatorKey = "fragmentCreator";
public static String fragmentCreationBroadcastMessage = "fragment-creation";
public static String fragmentDialogCreationBroadcastMessage = "fragment-dialog-creation";
protected Bundle arguments;
protected Boolean hasBundle;
public FragmentCreator(Bundle arguments, boolean hasBundle)
{
this.arguments = arguments;
this.hasBundle = hasBundle;
}
protected FragmentCreator(Parcel in)
{
hasBundle = (Boolean) in.readSerializable();
if (hasBundle == true && arguments == null)
{
arguments = in.readBundle();
}
}
public Fragment createFragment()
{
Fragment fragment = instantiateFragment();
if (arguments != null)
{
fragment.setArguments(arguments);
}
return fragment;
}
protected abstract Fragment instantiateFragment();
#Override
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags)
{
dest.writeSerializable(hasBundle);
if (arguments != null)
{
arguments.writeToParcel(dest, 0);
}
}
public void sendFragmentCreationMessage(Context context)
{
Intent intent = new Intent(FragmentCreator.fragmentCreationBroadcastMessage);
intent.putExtra(FragmentCreator.fragmentCreatorKey, this);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
public void sendDialogFragmentCreationMessage(Context context)
{
Intent intent = new Intent(FragmentCreator.fragmentDialogCreationBroadcastMessage);
intent.putExtra(FragmentCreator.fragmentCreatorKey, this);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
}
This way, a Fragment that is created looks like this:
public class TemplateFragment extends Fragment implements GetActionBarTitle, View.OnClickListener
{
private int titleId;
public TemplateFragment()
{
titleId = R.string.app_name;
}
#Override
public int getActionBarTitleId()
{
return titleId;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_template, container, false);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
}
#Override
public void onClick(View v)
{
}
public static class Creator extends FragmentCreator
{
public Creator()
{
super(null, false);
}
public Creator(Bundle bundle)
{
super(bundle, true);
}
protected Creator(Parcel in)
{
super(in);
}
#Override
protected Fragment instantiateFragment()
{
return new TemplateFragment();
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<TemplateFragment.Creator> CREATOR = new Parcelable.Creator<TemplateFragment.Creator>()
{
#Override
public TemplateFragment.Creator createFromParcel(Parcel in)
{
return new TemplateFragment.Creator(in);
}
#Override
public TemplateFragment.Creator[] newArray(int size)
{
return new TemplateFragment.Creator[size];
}
};
}
}
The initial container activity that can process the messages looks like this:
Intent intent = new Intent();
intent.setClass(this.getActivity(), ContainerActivity.class);
intent.putExtra(FragmentCreator.fragmentCreatorKey,
new TemplateFragment.Creator());
startActivity(intent);
And the Fragments "instantiate other Fragments" like this:
Bundle bundle = new Bundle();
bundle.putParcelable("argument", data);
TemplateFragment.Creator creator = new TemplateFragment.Creator(bundle);
creator.sendFragmentCreationMessage(getActivity());
And the Container Activity receives the instantiation request:
public class ContainerActivity extends ActionBarActivity implements SetFragment, ShowDialog
{
private BroadcastReceiver mFragmentCreationMessageReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
setFragment((FragmentCreator) intent.getParcelableExtra(FragmentCreator.fragmentCreatorKey));
}
};
private BroadcastReceiver mFragmentDialogCreationMessageReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
showDialog((FragmentCreator) intent.getParcelableExtra(FragmentCreator.fragmentCreatorKey));
}
};
#Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
this.setContentView(R.layout.activity_container);
getActionBar().setDisplayHomeAsUpEnabled(true);
if (saveInstanceState == null)
{
Fragment fragment = ((FragmentCreator) getIntent().getParcelableExtra(
FragmentCreator.fragmentCreatorKey)).createFragment();
if (fragment != null)
{
replaceFragment(fragment);
}
}
else
{
this.getActionBar()
.setTitle(
((GetActionBarTitle) (this.getSupportFragmentManager()
.findFragmentById(R.id.activity_container_container)))
.getActionBarTitleId());
}
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
}
#Override
protected void onResume()
{
LocalBroadcastManager.getInstance(this).registerReceiver(mFragmentCreationMessageReceiver,
new IntentFilter(FragmentCreator.fragmentCreationBroadcastMessage));
LocalBroadcastManager.getInstance(this).registerReceiver(mFragmentDialogCreationMessageReceiver,
new IntentFilter(FragmentCreator.fragmentDialogCreationBroadcastMessage));
super.onResume();
}
#Override
protected void onPause()
{
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mFragmentCreationMessageReceiver);
LocalBroadcastManager.getInstance(this).unregisterReceiver(
mFragmentDialogCreationMessageReceiver);
}
#Override
public void setFragment(FragmentCreator fragmentCreator)
{
Fragment fragment = fragmentCreator.createFragment();
replaceFragment(fragment);
}
public void replaceFragment(Fragment fragment)
{
if (fragment != null)
{
this.setTitle(((GetActionBarTitle) fragment).getActionBarTitleId());
getSupportFragmentManager().beginTransaction()
.replace(R.id.activity_container_container, fragment).addToBackStack(null).commit();
}
}
#Override
public void showDialog(FragmentCreator fragmentCreator)
{
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fragmentCreator.createFragment();
if (fragment instanceof DialogFragment)
{
DialogFragment df = (DialogFragment) fragment;
df.show(fm, "dialog");
}
else
{
Log.e(this.getClass().getSimpleName(), "showDialog() called with non-dialog parameter!");
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == android.R.id.home)
{
this.onBackPressed();
}
return super.onOptionsItemSelected(item);
}
}
My question is, is this actually a good idea, or is this a terrible case of "over-engineering" (creating a Factory for each Fragment and sending it to the Activity in the form of a local broadcast, rather than just casting the Activity of the most possible holder activity's interface and calling the function like that)?
My goal was that this way, I can use the same Activity for holding "branch" fragments, so that I don't need to make one for each menu point. Rather than just re-use the same activity, and divide all logic into fragments. (Currently it doesn't support orientation-based layout organization, I see that downside - and also that this way each Fragment needs to hold a static creator, which is extra 'boilerplate code').
If you know the answer why I shouldn't be using the local broadcast manager for this, I'll be happy to hear the response. I think it's pretty neat, but there's a chance it's just overcomplicating something simple.
You can use Interface for it so main objective of Fragment re-usability is maintained. You can implement communication between Activity-Fragment OR Fragment-Fragment via using following :
I am asuming that your moto is Fragment to communicate with its Activity and other Fragments.
If this is the case please go throught it.
To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.
Example :
# In fragment
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallback = (OnHeadlineSelectedListener) activity;
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
mCallback.onArticleSelected(position);
}
}
# In Activity
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{
public void onArticleSelected(int position) {
// Do something here
}
}
Link: http://developer.android.com/training/basics/fragments/communicating.html
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