how are you ? so i have a problem calling my custom dialog fragment (cdf) from another cdf, when i call the cdf from a FragmentActivity it works fine.
this is the method i use to call the cdf from FragmentActivity.
private void openDatePicker(int idView) {
Bundle bundle = new Bundle();
bundle.putInt("VIEW", idView);
DialogFragment newFragment = new DatePicker();
newFragment.setArguments(bundle);
newFragment.show(getActivity().getFragmentManager(), "datePicker");
}
this is the onCreate method from the first cdf
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_add, null);
ButterKnife.bind(this, view);
builder.setView(view);
when i called it from cdf, it gives me an error
Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
i am assuming it has something to do with the context, my cdf doesnt know the context of other cdf so it doesnt know what to load where. so how do i call cdf from another cdf exactly?.
Passing view from one fragment to another is not the correct way. The view in the 1st fragment can be destroyed when the second fragment is shown depending on whether you add/replace the fragment. Check the fragment lifecycle here in the android doc.
Pass the value (instead of view or view id) to the second fragment as Bundle using setArguements method.
Related
What I ought to do is change the view/layout of Fragment without creating another class for fragment on click of a button.
For example I have an activity - ContactsActivity and I have a fragment - ContactsFragment.
The Standard way of using Fragments:
From ContactsActivity I call ContactsFragment by -
getFragmentManager().beginTransaction().replace(android.R.id.content, new ContactsFragment())
.commit();
Code for setting View in ContactsFragment class -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
return rootView;
}
**Now comes how I do what I want to do ** (Change the view of fragment)
I change only the view of ContactsFragment by doing a bad kind of hack.
I change the onCreateView() shown above to this -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
//Set the view to R.layout.contacts_primary
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
//Set the view to R.layout.contacts_secondary
if(getActivity().getIntent()!=null && getActivity().getIntent().getBooleanExtra("s", false)) {
rootView = inflater.inflate(R.layout.contacts_secondary, container, false);
Log.e(tag,getActivity().getIntent().getExtras().toString());
return rootView;
}
//This is the onClickListener which again calls the ContactsActivity class,
//this time with an Intent which I used above to change the view from
//R.layout.contacts_primary to R.layout.contacts_secondary
Button button = (Button)rootView.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getActivity(), ContactsActivity.class).putExtra("s",true));
}
});
Now everything works as I want and flawlessly.
But I have a very strong feeling that either all of it is wrong and Fragments aren't supposed to work this way or I am using a hectic hack to achieve what can be done by few lines of code.
So please let me know what is it? And if there is a standard way of doing what I am trying to do.
For me passing additional argument on which base fragment decides wich layout to use seems totally ok. But there is cleaner way of doing what you want to achieve without starting another activity.
First of all pass argument to fragment by making standard static new instance method in fragment (we cannot pass this argument in constructor as android always recreates fragments using empty constructor). Something like this:
public static ContactsFragment newInstance(boolan firstView) {
ContactsFragment fragment = new ContactsFragment();
Bundle args = new Bundle();
args.putBoolean("yourArg", firstView);
fragment.setArguments(args);
return fragment;
}
Every time you have to initiate your fragment do this with this method.
Then declare interface in your fragment to communicate with your activity. Like this
public interface NewViewListener {
public void showNewView(boolen firstView);
}
Than make your activity implement it so your activity han a method where it can place new fragment in container view. In your fragments onAttach and onDetach methodsmake sure your activity implements this interface and hold reference to your activity in private NewViewListener field in your fragment. Like this:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (NewViewListener ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement NewViewListener ");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
Then in on button click method call showNewView method on your activity with whatever argument you want indicating which view you want in new fragment instance. And in your activity method showNewVew fragment in the container. Like this:
#Override
public void showNewView(boolean firstView) {
getFragmentManager().beginTransaction().replace(android.R.id.content, ContactsFragment.newInstance(firstView)
.commit();
}
In your fragments onCreateView you may get passed arguments and decide which view you want to use.
I have a view pager that has 3 fragments inside.
However, the first fragment has a listview.
I want to open a new fragment when I click on a ListView item.
I was able to make it work succefully with this code:
Fragment FullBrand = new FullBrand();
FragmentTransaction ft = getFragmentManager()
.beginTransaction();
ft.replace(R.id.framebrands, FullBrand);
Bundle bundle = new Bundle();
FullBrand.setArguments(bundle);
ft.addToBackStack(null);
ft.commit();
However, when the new fragment launches, the viewpager is still there!
What to do? how can I get rid off them??
Thanks!
Seems like you're trying to replace one fragment inside the view pager.
If you want to replace the view pager fragment (with it's 3 child) and to show other fragment you need to call the transaction in the FragmentActivity and replace it in the current container.
Add callback to the activity and replace the whole container in the activity when listview item clicked.
Example to add listener
in view pager fragment, declare your interface:
public interface OnViewPagerClickListener {
public void onBlaBla(Object arg);
}
in your Fragment Activity:
public class MyFragmentActivity implement ViewPagerFragment.OnViewPagerClickListener
in your view pager fragment override onAttach & declare interface member
private OnViewPagerClickListener mCallback;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallback =(OnViewPagerClickListener) activity **try catch this code**
}
And wherever you want call the mCallback.onBlaBla(...)
and do the rest in the activity....
This is very summarize lesson for interfaces :)
More info about interface and callback here
Good luck.
You shouldn't try to remove the ViewPager instead better you can show the new Fragment (i.e FullBrand) in a DialogFragment . so If you click back it will bring you to old ViewPager only, it won't exit the app
To show a Dialog Fragment with FullScreen try the following code:
public class NewFragment extends DialogFragment {
private Dialog dialog;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.new_fragment_xml_here, container,
false);
return view;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
dialog = new Dialog(getActivity(),
android.R.style.Theme_Black_NoTitleBar);
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
}
Then call this NewFragment from your ListView item click to show this as NewScreen which will look like a new screen:
NewFragment fragment = new NewFragment();
fragment.show(fragmentManager, "NewFragment");
I hope it would help you.
I have a few Actiobar with 5 tabs each with a fragment. In 3 of this fragments I want to show a Dialog so I've created a new class:
public static class MyDialogFragment extends DialogFragment {
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
static MyDialogFragment newInstance() {
return new MyDialogFragment();
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int style = DialogFragment.STYLE_NORMAL;
int theme = android.R.style.Theme_Holo_Dialog;
setStyle(style, theme);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_dialog, container, false);
View tv = v.findViewById(R.id.textV);
((TextView)tv).setText("Dialog using style Normal - Theme AlertDialog - NoActionBar");
return v;
}
}
In every onCreate method of this 3 fragments I'm trying to show the Dialog by using this method:
private void showPopup()
{
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("dialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
DialogFragment newFragment = MyDialogFragment.newInstance();
newFragment.show(ft, "dialog");
}
Now the problem is that this dialog is displayed on tabs that should not.
For example I want tabs 1 3 and 5 to display the Dialog - and sometimes it displays it - but sometimes when I tap the tab 2 this dialog appears and if I tap 3 the Dialog is not showed.
What could be the problem and how should I fix it? Thanks
Have you try to move your showPopup() call in onCreateView() or in onActivityCreated() methods, instead of in onCreate() one ?
EDIT: According to comments below, the problem is linked to the use of a ViewPager, which prepare some next Fragments to be viewed, and then call onCreate() methods.
So I've found a solution - in every fragment I override a method called setMenuVisibility - and test if the the fragment is visible. If it is - I call my method.
I am trying to set the title of a DialogFragment from the onActivityCreated method of a Fragment extending ListFragment.
public void onActivityCreated(Bundle savedState) {
super.onActivityCreated(savedState);
ListView lv = this.getListView();
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int pos, long id) {
android.app.FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
RatingDialogFragment newFragment = new RatingDialogFragment();
newFragment.getDialog().setTitle("String");
fragmentTransaction.add(newFragment, "dialog");
newFragment.show(fragmentTransaction, "dialog");
return true;
}
});
}
This produces a null pointer exception because the DialogFragment's mDialog is still null.
See: DialogFragment.getDialog returns null
Any ideas on how to fix this?
One possible way to fix this is to let the Fragment being created decide when it's safe to set its title.
For example, you could pass the title in the constructor and keep it in a member variable.
RatingDialogFragment newFragment = new RatingDialogFragment("String");
Then in your RatingDialogFragment:
...
public RatingDialogFragment(String title) {
mTitle = title;
}
...
Somewhere in your RatingDialogFragment lifecycle, when it's safe to do so, actually set the title.
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Set title for this dialog
getDialog().setTitle(mTitle);
}
Edit: It is worth noting that although this works in this particular case, a better general approach is to use Fragment's ability to receive arguments via the setArguments() and getArguments() methods. This allows you to keep the default empty constructor there so that your fragment can be instantiated by the framework correctly (eg if you were introducing your Fragment via XML). An example can be found here.
From the documentation:
Every fragment must have an empty constructor, so it can be
instantiated when restoring its activity's state. It is strongly
recommended that subclasses do not have other constructors with
parameters, since these constructors will not be called when the
fragment is re-instantiated; instead, arguments can be supplied by
the caller with setArguments(Bundle) and later retrieved by the
Fragment with getArguments().
I am trying to get the Dialog I have created with an extended DialogFragment using DialogFragment.getDialog() but it returns null.
Basically I want to alter the text in the layout from the FragmentActivity which creates and shows the DialogFragment.
You're calling getDialog() too early in the DialogFragment's life cycle.
getDialog() simply returns the private variable mDialog from the DialogFragment.
When a DialogFragment is instantiated mDialog is null, and then it gets set when onCreateDialog is fired inside getLayoutInflater(Bundle savedInstanceState), so you have to call getDialog after onCreateDialog.
For example, the order of some common methods called is onCreate, onCreateDialog, and onCreateView, onStart. So, you can call getDialog and have it return something in onCreateView or onStart, but not in onCreate or onCreateDialog.
Even though onStart is called called when the Fragment is visible to the user, adjusting the layout of the fragment at that point looks fine.... for example setting the width and height using getDialog().getWindow().setLayout(..., ...); doesn't make the fragment appear to change size, but just appears to have the newly set size.
Try calling executePendingTransactions() from the available FragmentManager.
dialogFragment = new DialogFragment();
...
dialogFragment.show(mFragmentActivity.getSupportFragmentManager(), "Dialog");
mFragmentActivity.getSupportFragmentManager().executePendingTransactions();
Dialog d = dialogFragment.getDialog()
...
There is 2 way to show DialogFragment:
void showDialog() {
// Create the fragment and show it as a dialog.
DialogFragment newFragment = MyDialogFragment.newInstance();
newFragment.show(getFragmentManager(), "dialog");
}
And
FragmentTransaction ft = getFragmentManager().beginTransaction();
DialogFragment newFragment = MyDialogFragment.newInstance();
ft.add(R.id.embedded, newFragment);
ft.commit();
You can only get a nonNull dialog when using the first way.
public class Dialog extends DialogFragment {
private DialogListener dialogListener;
public void setDialogListener(DialogListener dialogListener) {
this.dialogListener = dialogListener;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.layout_dialog, null);
return view;
}
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (null != dialogListener) {
dialogListener.onDismiss();
}
}
public interface DialogListener {
void onDismiss();
}
}
in Activity
...
Dialog dialog= new Dialog();
dialog.setDialogListener(new Dialog.DialogListener() {
#Override
public void onDismiss() {
Foo()..
}
});
One reason for why getDialog() might return null after the dialog has been constructed and properly stored in mDialog is an accidental invocation of dismiss() on the DialogFragment.
When dismiss() is called, it will reset the mDialog field to null so that subsequent invocations of getDialog() will return null instead of the previously constructed dialog.
In my case, dismiss() was called to handle an error situation / side-case in the DialogFragment's onActivityCreated() method. Subsequently trying to use getDialog() from the onResume() method returned null.
Also refer to the source code of the DialogFragment class, specifically its dismissInternal(boolean allowStateLoss) method:
https://github.com/aosp-mirror/platform_frameworks_base/blob/pie-platform-release/core/java/android/app/DialogFragment.java