So I am using a custom dialog fragment, when it pops up it asks a question and when the positive response "yes" is selected I want to load a specific fragment based on which ActionTab is selected. Obviously right now the code just loads 1 default fragment but I would be implementing a switch statement that checked either which Tab was selected or which fragment was currently visible. My question is there a preferred method for doing this? As in selecting .isVisible() for the fragment, or using the .getSelectedTab() method for the current ActionTab.
Here is my code, and thank you in advance for your time
current .java
public class StoreDialog_Fragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder storeD = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
storeD.setView(inflater.inflate(R.layout.fragment_store_dialog, null))
.setMessage(R.string.store_question)
.setPositiveButton(R.string.yes_q,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// switch statement here
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.replace(R.id.header_fragment_container,
new Assets_Fragment());
ft.commit();
}
})
.setNegativeButton(R.string.no_q,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// No it isn't!
}
});
// Create the Dialog object and return it
return storeD.create();
}
}
isVisible() is for when you are physically removing a view that you may not need anymore until later; tied to View.Visible, View.Invisible, View.Gone. So you would be better off checking against the selected tab for what you are doing.
Additionally, as per your request you can simply find the ActionBar from the Activity easy enough and make small modifications like the following as per your followup question:
actionBar_ = getActionBar();
actionBar_.setSubtitle("subtitle");
actionBar_.setTitle("title");
actionBar_.setDisplayHomeAsUpEnabled(true);//If navigation from home needed
actionBar_.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#BB000000"))); //Allow for some transparency in the ActionBar.
Related
I have a custom dialog which extends AppCompatDialogFragment. I am calling it from within one Fragment of a BottomNavigationBar. It gets dismissed when a message is returned from a ListenerService. It all works well until I switch to a different fragment using one of the icons on the BottomNavigationBar and then back to the fragment with Dialog functionality. The next time I show the dialog the message does not dismiss it.
I have a member variable:
SendingMessageDialog sendingMessageDialog;
When I click a button it calls this to show the dialog:
sendingMessageDialog = new SendingMessageDialog();
sendingMessageDialog.show(getActivity().getSupportFragmentManager(), "Sending Message");
When the message is receive I call this:
sendingMessageDialog.dismiss();
sendingMessageDialog = null;
I have also tried it this way:
private void dismissDialog() {
Fragment prev = getActivity().getSupportFragmentManager().findFragmentByTag("Sending Message");
if (prev != null) {
Log.d(TAG, "dismissDialog: prev is not null");
SendingMessageDialog df = (SendingMessageDialog) prev;
df.dismiss();
}
}
My SendingMessageDialog which extends AppCompatDialogFragment, I have this code in my onCreateDialog:
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateDialog: starting");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.send_message_dialog, null);
builder.setView(view)
.setTitle("Sending Token")
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
return builder.create();
}
I have looked up a few post on stackoverflow but none seem to address this specific issue. I saw this one, but there is no cancel() method available. I saw this one but I have to dismiss the dialog when I get a message from another part of the system, so I cannot just do this in-line sort of thing. This one just seemed like a simple mistake of calling show on the wrong dialog. I couldn't quite get a handle on this one, but it's 8 years old and seems to be about the coordination of two dialogs.
Many of the examples identify the dismiss is not working. In my case it is, until I switch fragments using the bottom navigation and then return to the fragment where the dialog process exists.
Nothing special happening in the navigation:
selectedFragment = new RunScenarioListFragment();
and:
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout_run_scenario,selectedFragment)
.addToBackStack(null)
.commit();
So I have a fragment (WifiSetupFragment) that calls a DialogFragment, and that DialogFragment needs to pass a string back to the original fragment. I know to do this you have an interface in the activity that will send data to the original fragment like so, which I am already doing:
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.addToBackStack(null);
transaction.replace(R.id.content_frag, WifiSetupFragment.newInstance(password));
transaction.commit();
So the first time I call WifiSetupFragment, I haven't created a DialogFragment yet because I haven't clicked on an item to open the dialog. My question is should I just call
WifiSetupFragment.newInstance(null)
and have a null check for the password string in my fragment? Because I don't have a password unless the DialogFragment is open, and it's not always open. If this made no sense, please tell me and I'll try to explain more clearly. I guess it just seems strange to me to have a parameter for a string that might only be sent to this fragment occasionally since the data isn't constantly being passed in.
You don't need to communicate between these Fragments through the Activity. What you can do instead:
Make your WifiSetupFragment.newInstance() accept no parameters.
Make WifiSetupFragment implement a new interface, let's call it OnPasswordSuppliedListener.
Once you create your DialogFragment instance, attach it to a getChildFragmentManager() instead of getFragmentManager().
Now inside of your DialogFragment subclass you can reference WifiSetupFragment by calling getParentFragment().
Cast getParentFragment() to your interface and voila!
Note: I'm assuming you're using Fragments from the support library. Otherwise please be aware that nested Fragments feature was introduced in the API 17.
Your dialog can define an interface allowing to send input password back to parent fragment / activity:
public class TestDialog extends DialogFragment {
private TextView mPasswordView;
private OnPasswordDefinedCallback mCallback;
public static TestDialog newInstance() {
TestDialog dialog = new TestDialog();
return dialog;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// inflate layout for your dialog (it must include edit text for password)
LayoutInflater inflater = getActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.dialog_test, null);
// getting ui elements from layout
mPasswordView = (TextView) layout.findViewById(R.id.txt_password);
// building dialog
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(layout);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
try {
mCallback = (OnPasswordDefinedCallback) getTargetFragment();
} catch (ClassCastException e) {
throw new ClassCastException("must implement OnPasswordDefinedCallback");
}
if (mCallback != null) {
// send password back to parent
mCallback.doPasswordDefined(mPasswordView.getText().toString());
}
dismiss();
}
});
return builder.create();
}
public interface OnPasswordDefinedCallback {
void doPasswordDefined(String password);
}
}
Then in WifiSetupFragment you can proceed as follows for opening PasswordDialog:
TestDialog dialog = TestDialog.newInstance();
dialog.setTargetFragment(WifiSetupFragment.this, 1);
dialog.show(getChildFragmentManager(), null);
WifiSetupFragment must of course implement interface OnPasswordDefinedCallback.
I had a working DialogFragment that was using an inner class to do a bunch of things on some objects, set menu icons etc. When i went to Android Studio i realised that was incorrect and i've been trying to change the inner class to be static.
In so doing, I am now trying to use onCreateDialog to, as per Google docs, "doPositiveClick" and "doNegativeClick", so that the calling MainActivity can do the work on those objects instead of the fragment doing it.
What is now confusing me however, is how do I set the layout in the fragment - I can enter a title, message and buttons as such:
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.alert_title)
.setMessage(R.string.alert_message)
.setPositiveButton(R.string.set,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((MainActivity)getActivity()).doPositiveClick();
}
}
)
.setNegativeButton(R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((MainActivity)getActivity()).doNegativeClick();
}
}
)
But previously I was doing the layout like:
final EditText input = new EditText(MainActivity.this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(20, 20);
input.setText("5");
input.setLayoutParams(lp);
input.setRawInputType(Configuration.KEYBOARD_QWERTY);
Problem is, where does this go in onCreateDialog ? The Google docs shows how to set text on a dialog textView, but that is within onCreateView().
My confusion is that the google doc doesnt do both, ie, it doesnt show how to both, set up custom elements, AND set up the positive/negative click in the calling MainActivity - or if it does, i'm sorry I cant see it right now.
So can anyone make it clearer for me, using the above onCreateDialog, how can I have an editText field, with a default value that takes user input, and then get back that input to the doPositiveClick() to process.
DialogFragment can use in 2 ways: dialog or view.
case1: use DialogFragment as a dialog. you have to implement onCreateDialog() to return a dialog. and then have to show the dialog in the following way. see the example:
public static class MyAlertDialogFragment extends DialogFragment {
public static MyAlertDialogFragment newInstance(int title) {
MyAlertDialogFragment frag = new MyAlertDialogFragment();
Bundle args = new Bundle();
args.putInt("title", title);
frag.setArguments(args);
return frag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int title = getArguments().getInt("title");
return new AlertDialog.Builder(getActivity())
.setIcon(R.drawable.alert_dialog_icon)
.setTitle(title)
.setPositiveButton(R.string.alert_dialog_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((FragmentAlertDialog)getActivity()).doPositiveClick();
}
}
)
.setNegativeButton(R.string.alert_dialog_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((FragmentAlertDialog)getActivity()).doNegativeClick();
}
}
)
.create();
}
}
Create and have to show dialog as the following way. show this way don't care, whether onCreateView() is implemented or not.
// Create the fragment and show it as a dialog.
DialogFragment newFragment = MyDialogFragment.newInstance();
newFragment.show(getFragmentManager(), "dialog");
case2: use as view (it is not feature of dialog). it is only view. you have to implement onCreateView() and show dialog as the following way:
public static class MyDialogFragment extends DialogFragment {
static MyDialogFragment newInstance() {
return new MyDialogFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.hello_world, container, false);
View tv = v.findViewById(R.id.text);
((TextView)tv).setText("This is an instance of MyDialogFragment");
return v;
}
}
and have to show view as follow. the same as use Fragment class. show this way don't care, whether onCreateDialog() is implemented or not.
FragmentTransaction ft = getFragmentManager().beginTransaction();
DialogFragment newFragment = MyDialogFragment.newInstance();
ft.add(R.id.embedded, newFragment);
ft.commit();
summary:
in design, you can implement onCreateView() and onCreateDialog() together and use the same source code with this DialogFragment lifecycle. If the screen is small, use DialogFragment as Dialog. If the screen is big, use DialogFragment as view (the same common Fragment class).
Notice that use the correct way to show DialogFragment to suitable with onCreateView() and onCreateDialog() to prevent exception.
sorry guys I thought I exhausted my searches but just after I posted this I was able to fix it, putting the text field/layout inside the onCreateDialog BEFORE the Builder and then doing setView() to that input as such:
**LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(20, 20);
final EditText input = new EditText(getActivity());
input.setText("5");
input.setLayoutParams(lp);
input.setRawInputType(Configuration.KEYBOARD_QWERTY);**
// Use the Builder class for convenient dialog construction
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.alert_title)
.setMessage(R.string.alert_message)
**.setView(input)**
.setPositiveButton(R.string.set,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((MainActivity)getActivity()).doPositiveClick();
}
}
)
Only question now is how do I get the value back after the user inputs ?
I am having problems referencing an already instantiated Fragment from within my Activity.
I have two tabs as part of the Action Bar and for each Tab I am instantiating an instance of the same fragment....
Fragment = Player
Tab1 = Player1 (Player Fragment with details from DB of Player 1)
Tab2 = Player2 (Player Fragment with details from DB of Player 2)
The Player fragment includes a field (Games played) which is amendable using a Number Picker.
So... I have created an interface which I have implemented on my activity. When the button is clicked on the Fragment it calls the interface which on the activity creates a Dialog Fragment to display the number picker. Once the number picker has been closed and passed back the number of Games Played to the Activity I then want to update this value on the correct Fragment (So if the button was pressed on Tab 2 then the value on the Player2 should get updated.)
This is where I am drawing a blank.
I am using actionBar.addTab to add each tab and using a TabsListener Class implementing ActionBar.TabListener to do a replace of the correct Fragment when each tab is pressed.
ActionBar handles the FragmentManager stuff for you...
What I think I need to do here within my Activity is get the Current Fragment so that I can make a call to a method in this fragment to update it. But when Adding the Fragment through the TabListener I cannot see a way that I can either get and store the ID of the Fragment instance or set a Tag for it. If I could then I could use getFragmentByID or getFragmentByTag to find it.
Any ideas on how I should do this.
I thought I had a completely different solution whereby I made by database update in the Number Picker itself and then simply let onResume() update the value in my Visible Fragment when the DialogFragment closes but it seems that onResume() is not called when the DialogFragment is closed.
I have not posted code examples as I hope the above simplifies the question.
Both the 'add' and 'replace' methods of FragmentTransaction have a number of overloads. Using the one with the parameters:
(int containerViewId, Fragment fragment, String tag)
...allows you to provide a tag name for the fragment that you can subsequently use to retrieve it with a call to findFragmentByTag.
You can specify the TabListener (http://developer.android.com/reference/android/app/ActionBar.TabListener.html) and get notified when tab is reselected and here you can update the UI.
In my opinion you should update the database after user selects the value from the dialog. You can specify OnClickListener for different buttons on dialog and get notified when something is selected or cancelled.
Example at http://developer.android.com/reference/android/app/DialogFragment.html#AlertDialog,
public static class MyAlertDialogFragment extends DialogFragment {
public static MyAlertDialogFragment newInstance(int title) {
MyAlertDialogFragment frag = new MyAlertDialogFragment();
Bundle args = new Bundle();
args.putInt("title", title);
frag.setArguments(args);
return frag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int title = getArguments().getInt("title");
return new AlertDialog.Builder(getActivity())
.setIcon(R.drawable.alert_dialog_icon)
.setTitle(title)
.setPositiveButton(R.string.alert_dialog_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((FragmentAlertDialog)getActivity()).doPositiveClick();
}
}
)
.setNegativeButton(R.string.alert_dialog_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((FragmentAlertDialog)getActivity()).doNegativeClick();
}
}
)
.create();
}
}
I'm doing a typical fragment activity with the left pane, Fragment A, as a list and the right pane, Fragment B as the content (on a tablet), and on the phone both are in one pane.
Everything works great on the tablet version. But on the phone version I run into a problem. My Fragment B consists of some TextViews and below them, a GridView. I'd like to update the TextViews every time the user clicks on a grid item. This works fine. The problem is in implementing an AlertDialog within the grid adapter:
OnClickListener clickListener = new OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext.getContext());
...
builder.setPositiveButton(mContext.getContext().getResources().getString(
R.string.ok), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
fragActivity.getSupportFragmentManager().executePendingTransactions();
FragmentB f = (FragmentB)
((MyActivity) fragActivity).getSupportFragmentManager()
.findFragmentByTag(FragmentB.TAG);
item.setAmount(helperDouble); //shouldn't be relevant to the problem
if (f != null) {
Log.i("GridAdapter", "f is not null, updating views");
f.updateViews();
} else {
Log.i("GridAdapter", "f is null, what the...");
}
}
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
};
FragmentB f returns fine every time on the tablet, but no matter what I seem to do, it always returns null on the phone. It seems similar to this SO post but I don't know if I can apply that to my situation. Here's how I add the fragment in MyActivity:
#Override
public void onCustomerSelected(Customer customer, int index) {
//update fragment here
if (isScreenSizeLarge()) { //if tablet:
FragmentB f;
//if fragment doesn't exist, create it
if (getSupportFragmentManager().findFragmentByTag(FragmentB.TAG) == null) {
f = FragmentB.newInstance(customer, index);
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.add(R.id.fragment_b_container, f, FragmentB.TAG);
trans.commit();
getSupportFragmentManager().executePendingTransactions();
} else {
f = (FragmentB) getSupportFragmentManager()
.findFragmentByTag(FragmentB.TAG);
}
} else { //if phone:
FragmentB newCartFrag = FragmentB.newInstance(customer, index);
FragmentTransaction newTrans = getSupportFragmentManager().beginTransaction();
newTrans.replace(R.id.fragment_container, newCartFrag);
newTrans.addToBackStack(FragmentB.TAG);
newTrans.commit();
getSupportFragmentManager().executePendingTransactions();
}
}
(Yes I'm aware I called executePendingTransactions() twice, I did just in case.)
So I'm assuming the problem has something to do with the fragment (or fragment activity) "losing focus" of the fragment. I just don't understand the difference between why it would find it on the tablet (which could lose focus because it has two fragments to worry about) but not the phone (wherein FragmentB is the only active fragment at the time).
In onCustomerSelected(), I forgot to add a TAG in the "replace" line.
Instead of
newTrans.replace(R.id.fragment_container, newCartFrag);
it should have been
newTrans.replace(R.id.fragment_container, newCartFrag, FragmentB.TAG);
Very small thing so it took forever for me to find.