I have an app that uses tabbed activity, with 3 tabs, each with a separate fragment and layout. Tab3 has some user-profile settings, like a name for example. Name is displayed inside a cardview, so I made that cardview clickable, and onClick it would open an alertDialog with an edit text to enter the name. to build this dialog I found 2 methods.
First:- (this is run once the cardview is clicked(On clicklistener)) and its working very well and I have no issues with it but it just doesn't feel like a best-practice
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
alert.setTitle("Enter your Username");
LayoutInflater inflater = getActivity().getLayoutInflater();
View view1 = inflater.inflate(R.layout.editname_layout, null);
alert.setView(view1);
final EditText input = view1.findViewById(R.id.editname);
alert.setPositiveButton("Save", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
textView.setText(input.getText().toString());
}
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Canceled.
}
});
alert.show();
Second:- is that I build a separate DialogFragment with an interface to send data back to fragment this one feels like a best practice but i have faced many issues with it, such as.. when data is sent back to my fragment, I can't use the method "textView.setText" on the received data as I receive it outside the onCreateView method and thus textView always returns null.
public class Dialog extends AppCompatDialogFragment {
private Context context;
public DialogListener listener;
public Dialog(Context context) {
this.context = context;
}
#NonNull
#Override
public android.app.Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.name_layout, null);
final EditText name = (EditText) view.findViewById(R.id.editname);
builder.setView(view)
.setTitle("Username")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
}
})
.setPositiveButton("Save", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
String text = name.getText().toString();
listener.data(text);
Toast.makeText(getActivity(), "Saved", Toast.LENGTH_SHORT).show();
}
});
return builder.create();
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
Fragment fragment = new Tab3();
listener = (DialogListener) fragment;
}
catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement d.l");
}
}
public interface DialogListener {
void data(String name);
}
}
So what I'm thinking is that I should go with the first method as it's easier, and it's easier to extract text from it but still not sure if it would be a good practice.
thanks in advance!
Prefer second solution:
You can have your custom UI elements.
You have access to lifecycle changes (onResume,onCreate,etc.).
It keeps showing even when orientation is changed (the first solution doesn't).
And for sending data to previous fragment you can create listener to fragment.
interface MyFragmentListener {
fun onSendBackData(data: Any)
}
class MyFragment:Fragment {
private var listener: MyFragmentListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
listener = when {
context is MyFragmentListener -> context
parentFragment is MyFragmentListener -> parentFragment as MyFragmentListener
else -> error("You should implement MyFragmentListener")
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button.setOnClickListener {
listener?.onSendBackData("Data")
}
}
}
class Activity : Activity(), MyFragmentListener {
override fun onSendBackData(data:Any) {
textView.text = data.toString()
}
}
It's not a question of best practice, it's just how you want to design your App.
The first solution: You can directly interact with your UI. e.g: Set the new value in textView. But if you write a lot of dialogs, think about to generify the logic (the second solution).
The second solution: If you want just a generic Dialog, you need the use-case for it = multiple calls for dialogs and don't forget: if you instantiate the Dialog class you need to give him the textView if you want to change it in the UI
Related
I want to finish the activity when custom dialog is cancelled or dismissed. But when I use .setOnDismissListener in other classes, it is never being reached inside. I've found several problems, but the solution was to override onDismiss method inside the customDialog class. But I do not need to override onDismiss method for every customDialog I create. What should I do?
This is the code I call in another class, but never receive message in log "setOnDismissListener".
customDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
Log.d(TAG, "setOnDismissListener");
}
});
My CustomDialog class:
public class CustomDialog extends Dialog {
private static final String TAG = "CustomDialog";
public CustomDialog(Context context, String title, String message) {
super(context);
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setPadding(10, 50, 10, 10);
textView.setText(title);
textView.setTextColor(Color.BLACK);
textView.setTextSize(20);
Typeface boldTypeface = Typeface.defaultFromStyle(Typeface.BOLD);
textView.setTypeface(boldTypeface);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder
.setCustomTitle(textView)
.setMessage(message)
.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog customDialog = builder.show();
TextView messageText = customDialog.findViewById(android.R.id.message);
if (messageText != null) {
messageText.setGravity(Gravity.CENTER);
messageText.setTextColor(Color.GRAY);
} else {
Log.w(TAG, "messageText is null");
}
}
}
Yeah, so if you are not using some API to parse information, or are using local variables I suggest you do whatever functionality you want to do in your onClickListener() Method.
The problem is that you are using your CustomDialog which itself extends the Dialog Class. But instead of using that you create a new alert dialog and build that. You dismiss it, but the dialog which is dismissed is not your custom dialog class, but the builder dialog you created in your constructor. Even if you fixed for that, it introduces unnecessary complications.
What I suggest you do is create the Intent in your onClickListener() function. The way to do that would be to change your constructor to support a callback listener. Simply put you cannot just add an onDismissListener() when the dialog you listen to is another one. What you can do is pass in the function that you want to do when the user dismisses the dialog as a special case. See below.
So, first, modify your constructor like this:
public CustomDialog(Context context, String title, String message,
DialogInterface.OnClickListener listener) {
super(context);
}
In your constructor paste your previous code:
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setPadding(10, 50, 10, 10);
textView.setText(title);
textView.setTextColor(Color.BLACK);
textView.setTextSize(20);
Typeface boldTypeface = Typeface.defaultFromStyle(Typeface.BOLD);
textView.setTypeface(boldTypeface);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder
.setCustomTitle(textView)
.setMessage(message)
.setPositiveButton("Ok", listener);
AlertDialog customDialog = builder.show();
TextView messageText = customDialog.findViewById(android.R.id.message);
if (messageText != null) {
messageText.setGravity(Gravity.CENTER);
messageText.setTextColor(Color.GRAY);
} else {
Log.w(TAG, "messageText is null");
}
What you do is where you used to create a new onClickListener() you pass in the listener parameter.
Go to your MainActivity or where you create your custom dialog. There do this:
CustomDialog customDialog = new CustomDialog(FirstActivity.this, "Title", "Message",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Do your functionality here.
Intent intent = new Intent(context, activity.class);
//Add any flags if you want
...
context.startActivity(intent);
//Or you can simply do context.finish();
}
});
When you don't want to pass a onClickListener()(meaning when you don't want to finish() the activity) pass in null.
It would work. If this is not what you wanted, tell me and I will fix it.
Make an interface like
interface OnUserInformedCallback {
fun positive()
fun negative()
}
and implement this in your activity and pass it to the dialog method from where you are gettingdialog and you will get callback of ok and cancle in you activity.
code is like that
fun alertDialog(
context: Context,
message: String,
positiveText: String,
isUa: Boolean,
callback: OnUserInformedCallback
): Dialog {
val dialog = Dialog(context, android.R.style.Theme_Translucent_NoTitleBar)
dialog.setContentView(R.layout.alert_dialog_layout)
dialog.window?.setGravity(Gravity.CENTER)
dialog.setCancelable(true)
dialog.setCanceledOnTouchOutside(true)
val tvOk: TextView = dialog.findViewById(R.id.visit)
val tvMsg: TextView = dialog.findViewById(R.id.mess)
tvMsg.text = message
tvOk.text = positiveText
dialog.findViewById<TextView>(R.id.cancel).setOnClickListener {
callback.negative()
}
tvOk.setOnClickListener {
callback.positive()
}
dialog.create()
return dialog
}
in java for default dialog
private AlertDialog getDialog(Activity context, String message, OnUserInformedCallback callbac) {
return new AlertDialog.Builder(context)
.setTitle(R.string.app_name).setMessage(message)
.setCancelable(true)
.setPositiveButton(android.R.string.yes, (dialog12, which) -> callbac.positive())
.setNegativeButton(android.R.string.yes, (dialog1, which) -> callbac.positive())
.create();
}
Hey fellow stackoverflowers!!!
I'm wondering what the best way to pass a string taken from a Dialog Fragment based on user input on the Dialog into the main activity which called the string?
Here's my specific example but it's really long so if you don't feel like going through it don't worry about everything below.
Here's my source code, I've ommitted the imports n stuff
public class GroupNameFragment extends AppCompatDialogFragment {
private EditText edittGroupName;
public static String GROUP_NAME = "com.example.mashu.walkinggroup.controller - groupName";
// When the views are inflated, get access to them
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
edittGroupName = Objects.requireNonNull(getView()).findViewById(R.id.edittGroupName);
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Get reference to fragment's layout
View view = LayoutInflater.from(getActivity())
.inflate(R.layout.group_name_layout, null);
// OK button listener
DialogInterface.OnClickListener listener = (dialog, which) -> {
if (which == DialogInterface.BUTTON_POSITIVE) {
// If OK pressed, create bundle to be accessed in OnDismissListener in MapActivity,
// which contains the groupName user inputted
String groupName = edittGroupName.getText().toString();
Bundle bundle = new Bundle();
bundle.putString(GROUP_NAME, groupName);
setArguments(bundle);
}
};
// Build alert dialog
return new AlertDialog.Builder(getActivity())
.setTitle("Choose your Group Name!")
.setView(view)
.setPositiveButton(android.R.string.ok, listener)
.create();
}
// Extracts groupName from the bundle set up in the onClickListener above
public static String getGroupName(GroupNameFragment dialog) {
Bundle bundle = getArguments();
return bundle.getString(GROUP_NAME);
}
}
What I attempted to do was to this: First, I get access to the EditText that the user will type in their response. Then I set the Dialog Listener for the OK button which creates a bundle using the setArguments function which contains the groupName when the user is done, which will be accessed in the other activity later on by using the static getGroupName function. Here's the function in the main activity which creates the Dialog and sets the onDismissListener
private void createGroupNameDialog() {
// Instantiate Dialog
// Support Fragment Manager for backwards compatibility
FragmentManager manager = getSupportFragmentManager();
GroupNameFragment dialog = new GroupNameFragment();
dialog.show(manager, "GroupNameDialog");
// OnDismissListener callback function to be run whenever dialog dismissed.
dialog.getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialogInterface) {
// Update groupName based on what user inputted and update marker name at origin
groupName = GroupNameFragment.getGroupName(dialog);
originMarker.setTitle(groupName);
}
});
}
I think the problem is in groupName = GroupNameFragment.getGroupName(dialog). I feel like theres a better way to get the bundle here, and it seems weird to use the function as static and then pass in specific instance of GroupNameFragment in order to get the bundle (wouldn't that instance be gone by then since it's being used in the "OnDismiss"?). Also, the app crashes the second createGroupNameDialog is called, but it doesn't crash and actually opens the dialog window if I comment out the OnDismissListener, so I'm sure the problems in there somewhere but I don't know why it crashes before the dialog box even opens since OnDismiss happens AFTER the user dismisses the Dialog Box.
Thanks!!!
I accomplished passing variables back using an interface and listeners. I'll show you how I handled it (although I used a DialogFragment, this should still work for AlertDialogs, and in this example I passed an integer, not a string, but it would work for any data type).
public class DialogFragmentOtherMedia extends DialogFragment {
int dialogResult;
//The interface is important!
public interface YesNoListener {
void onYesOtherMedia(int output);
void onNoOtherMedia(int output);
}
//Checking for ClassCastException is nice here.
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof YesNoListener)) {
throw new ClassCastException(activity.toString() + " must implement YesNoListener");
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
dialogResult = 0;
final String mediaType[] = {getString(R.string.Ringtones),getString(R.string.Music),getString(R.string.Alarms)};
return new AlertDialog.Builder(getActivity())
.setTitle(getString(R.string.Select_Other_Media_Type))
.setSingleChoiceItems(mediaType, dialogResult, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Log.d("DialogFragmentOtherMedia.onCreateDialog","Item clicked: " + mediaType[which]);
dialogResult = which;
}
})
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Casting the activity to YesNoListener is very important here!
//You'll register the listener in the activity later, by implementing the interface.
((YesNoListener) getActivity()).onYesOtherMedia(dialogResult);
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Same thing for your other callbacks.
((YesNoListener) getActivity()).onNoOtherMedia(dialogResult);
}
})
.create();
}
}
Then you just need to implement it in your activity where you called the dialog from:
public class AlarmDetailsActivity extends Activity
DialogFragmentOtherMedia.YesNoListener {
//All of your activity stuff here...
#Override
public void onYesOtherMedia(int result) {
Log.i("Tag", "onYes Result: " + result);
}
#Override
public void onNoOtherMedia(int result) {
Log.i("Tag", "onNo Result: " + result);
}
}
Sorry about all of the random strings and extra alert dialog. I just wanted to show some actual working code from my app. I tried to add comments next to the important stuff. Hope this helps!
so i looked at the Android tutorial for Dialogs Link.
And Decided to make a custom Dialog to ask a user for a host, username and password to connect to. I dont want to connect to the host within the Dialog Class so i need to pass the strings back to the main activity. I tried to to that with an Interface just like in the tutorial but when i want to handle the event in the main activity accessing the EditText i get a NullPointerException. Im sure there is an easy workaround or Im making some other stupid mistake. Please help!
Dialog Class:
public class ConnectionDialog extends DialogFragment {
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
// Use this instance of the interface to deliver action events
NoticeDialogListener mListener;
// Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
mListener = (NoticeDialogListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString()
+ " must implement NoticeDialogListener");
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
final LayoutInflater inflater = getActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(inflater.inflate(R.layout.connection_dialog, null))
// Add action buttons
.setPositiveButton(R.string.connect, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogPositiveClick(ConnectionDialog.this);
// sign in the user ...
}
})
.setNegativeButton(R.string.abort, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogNegativeClick(ConnectionDialog.this);
//LoginDialogFragment.this.getDialog().cancel();
}
});
return builder.create();
}
}
And Main Activity:
public class MainActivity extends FragmentActivity implements ConnectionDialog.NoticeDialogListener {
public void showConnectionDialog() {
// Create an instance of the dialog fragment and show it
DialogFragment dialog = new ConnectionDialog();
dialog.show(getFragmentManager(), "Connection Dialog");
}
public void toast(String message){
Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, message, duration);
toast.show();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
EditText editHost = (EditText)dialog.getView().findViewById(R.id.dialog_host);
EditText editUser = (EditText)dialog.getView().findViewById(R.id.dialog_user);
EditText editPassword = (EditText)dialog.getView().findViewById(R.id.dialog_password);
String host = editHost.getText().toString();
String user = editUser.getText().toString();
String password = editPassword.getText().toString();
dialog.dismiss();
}
#Override
public void onDialogNegativeClick(DialogFragment dialog) {
toast("cancled");
dialog.dismiss();
// User touched the dialog's negative button
}
public void connect(View view){
showConnectionDialog();
}
}
Thanks a lot!
Edit:
NullPointerException
04-12 18:52:00.278 5216-5216/com.example.josias.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.josias.myapplication, PID: 5216
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
at com.example.josias.myapplication.MainActivity.onDialogPositiveClick(MainActivity.java:44)
at com.example.josias.myapplication.ConnectionDialog$2.onClick(ConnectionDialog.java:54)
at android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:153)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Yes You can.
Your interface will look like this,
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
}
Pass DialogFragment instance like this to the interface, in positive button onClick () method.
.setPositiveButton(R.string.add, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
noticeDialogListener.onDialogPositiveClick(NoticeDialog.this);
}
})
Next in your MainActivity - you can have every field of DialogFragment that you have displayed as forms. In interface method that you will override in MainActivity, define like this,
#Override
public void onSaveButtonClick(DialogFragment dialog) {
......
EditText field1= (EditText) dialog.getDialog().findViewById(R.id.field1);
EditText field2= (EditText) dialog.getDialog().findViewById(R.id.field2);
....
}
Then you can get the Text and work as per your requirement. For more information you can refre this one: https://www.youtube.com/watch?v=a0T--DfR48A Here everything is explained well...
Switch out your interfaces to pass the String values instead of passing along the entire dialog.
To get you started, do something like this
public interface NoticeDialogListener {
public void onDialogPositiveClick(String host, String user, String pass);
public void onDialogNegativeClick();
}
final LayoutInflater inflater = getActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
View dialogView = inflater.inflate(R.layout.connection_dialog, null);
final EditText editHost = (EditText)dialogView.findViewById(R.id.dialog_host);
final EditText editUser = (EditText)dialogView.findViewById(R.id.dialog_user);
final EditText editPassword = (EditText)dialogView.findViewById(R.id.dialog_password);
builder.setView(dialogView)
// Add action buttons
.setPositiveButton(R.string.connect, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
String host = editHost.getText().toString();
String user = editUser.getText().toString();
String pass = editPassword.getText().toString();
mListener.onDialogPositiveClick(host, user, pass);
}
})
// set null to dismiss dialog
.setNegativeButton(R.string.abort, null);
return builder.create();
You should use:
dialog.getDialog()
instead of:
dialog.getView()
I realize this issue has been touched on numerous times but nothing I try is working for me. I still get errors when trying to access SharedPreferences.
From the main Activity (McsHome) I am launch a variety of Dialogs to help the user add a location.
The first Dialog is below, this simply pops up a message stating a location needs to be added (PopupMessage.java):
public class PopupMessage extends DialogFragment {
String message = "";
AddLocation addLocation;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
addLocation = new AddLocation();
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(message)
.setPositiveButton("Add Location", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
addLocation.show(getFragmentManager(), "PopupMsgFragment");
}
})
.setNegativeButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
//
};
});
// Create the AlertDialog object and return it
return builder.create();
}
}
This gives the user an option to add a location, when that button is clicked another dialog pops up (AddLocation.java):
public class AddLocation extends DialogFragment {
EditText mcsDomain;
EditText friendlyName;
EditText password;
ProcessLocation addLoc;
String message = "";
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.add_location_dialog, null); // Pass null as the parent view because its going in the dialog layout
mcsDomain = (EditText) layout.findViewById(R.id.mcsDomain);
friendlyName = (EditText) layout.findViewById(R.id.friendlyName);
password = (EditText) layout.findViewById(R.id.password);
builder.setView(layout)
.setTitle("Add/Update Location")
// Add action buttons
.setPositiveButton("Add/Update", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
// Passes the chosen location parameters to the ProcessLocation class
addLoc.processLocation(mcsDomain.getText().toString(),friendlyName.getText().toString(),password.getText().toString());
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
return builder.create();
}
The AddLocation.java uses an XML layout which includes 3 EditText fields. The values of these are passed on to a third Class, ProcessLocation.java which includes the method processLocation().
public class ProcessLocation {
SharedPreferences domainToName;
SharedPreferences nameToDomain;
public void processLocation(String domain, String name, String password) {
domainToName = getSharedPreferences("domainToName", MODE_PRIVATE);
nameToDomain = getSharedPreferences("nameToDomain", MODE_PRIVATE);
// final Editor domainEdit = domainToName.edit();
// final Editor nameEdit = nameToDomain.edit();
if (nameToDomain.contains(name)) {
System.out.println("Name Doesn't Exist");
}
}
}
I'm getting an error on the MODE_PRIVATE, I believe related to Context. I've been playing around with context for hours with no luck (or understanding). I know I'm popping up a couple of dialogs in a row. If I add "extends Activity" the error goes away but then the app crashes when trying to getSharedPreferences.
From going through the other posts I'm sure it's to do with passing the context from my McsHome.java activity but everything I've tried has failed.
First of all, in AddLocation you declare the member variable addLoc, but you never assign it to anything. If you did get this to compile, it would throw a NullPointerException here:
addLoc.processLocation(mcsDomain.getText().toString(), friendlyName.getText().toString(),
password.getText().toString());
getSharedPreferences() is a method of the Context class. In ProcessLocation.processLocation() you are trying to call it. This method doesn't exist in the ProcessLocation class.
You need to do the following:
1) ProcessLocation needs to have a Context reference, so that it can call getSharedPreferences(). The easiest way to do this is to declare a member variable in ProcessLocation of type Context and have it initialized in the constructor of ProcessLocation. Like this:
public class ProcessLocation {
Context context;
SharedPreferences domainToName;
SharedPreferences nameToDomain;
// Constructor
ProcessLocation(Context context) {
this.context = context;
}
2) You need to create an instance of ProcessLocation. In AddLocation, before using the variable addLoc you will need to initialize it. Like this:
// Create instance of ProcessLocation and pass it the activity (Activity is a Context)
addLoc = new ProcessLocation(getActivity);
3) Use the Context in ProcessLocation.processLocation(), like this:
public void processLocation(String domain, String name, String password) {
domainToName = context.getSharedPreferences("domainToName", Context.MODE_PRIVATE);
nameToDomain = context.getSharedPreferences("nameToDomain", Context.MODE_PRIVATE);
...
}
It is late and I'm tired and I didn't put this through a compiler, so please forgive me if I left out a comma or a semicolon or spelled something wrong. Hopefully you get the drift. Good luck!
I have an alert dialog with a single-choice list and two buttons: an OK button and a cancel button. The code below show how I implemented it.
private final Dialog createListFile(final String[] fileList) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Compare with:");
builder.setSingleChoiceItems(fileList, -1, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Log.d(TAG,"The wrong button was tapped: " + fileList[whichButton]);
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {}
});
return builder.create();
}
My goal is to obtain the name of the selected radio button when the OK button is tapped. I tried to save the string in a variable, but inside an inner class it is possible to access only final variables. Is there a way to avoid using a final variable to store the selected radio button?
Using a final variable obviously won't work (since it can only be assigned once, at declaration time). So-called "global" variables are usually a code smell (especially when they become part of an Activity class, which is usually where AlertDialogs are created).
The cleaner solution is to cast the DialogInterface object to an AlertDialog and then call getListView().getCheckedItemPosition(). Like this:
new AlertDialog.Builder(this)
.setSingleChoiceItems(items, 0, null)
.setPositiveButton(R.string.ok_button_label, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
int selectedPosition = ((AlertDialog)dialog).getListView().getCheckedItemPosition();
// Do something useful withe the position of the selected radio button
}
})
.show();
This has been answered just fine, but I keep finding this answer from Google and I wanted to share a non-anonymous class solution. I prefer reusable classes myself and may be helpful to others.
In this example, I'm using a DialogFragment implementation and retrieving a value via a callback method.
The callback method to get values from a Dialog can be done by creating a public interface
public interface OnDialogSelectorListener {
public void onSelectedOption(int selectedIndex);
}
Also the DialogFragment implements DialogInterface.OnClickListener which means you can register the class you've implemented as the OnClickListener for the DialogFragment that is being created.
For example
public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
builder.setTitle(R.string.select);
builder.setSingleChoiceItems(mResourceArray, mSelectedIndex, this);
builder.setPositiveButton(R.string.ok, this);
builder.setNegativeButton(R.string.cancel, this);
return builder.create();
}
The line
builder.setSingleChoiceItems(mResourceArray, mSelectedIndex, this);
Creates a choice dialog with the options from a resource array stored in mResourceArray. This also preselects an option index from what is stored in mSelectedIndex and finally it sets this itself as the OnClickListener. (See full code at the end if this paragraph is a tad confusing)
Now, the OnClick method is where you grab the value that comes from the dialog
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case Dialog.BUTTON_NEGATIVE: // Cancel button selected, do nothing
dialog.cancel();
break;
case Dialog.BUTTON_POSITIVE: // OK button selected, send the data back
dialog.dismiss();
// message selected value to registered callbacks with the
// selected value.
mDialogSelectorCallback.onSelectedOption(mSelectedIndex);
break;
default: // choice item selected
// store the new selected value in the static variable
mSelectedIndex = which;
break;
}
}
What happens here is when an item is selected, it's stored in a variable. If the user clicks the Cancel button, no update is sent back and nothing changes. If the user clicks the OK button, it returns the value to the Activity that created it via the callback created.
As an example, here is how you would create the dialog from a FragmentActivity.
final SelectorDialog sd = SelectorDialog.newInstance(R.array.selector_array, preSelectedValue);
sd.show(getSupportFragmentManager(), TAG);
Here, the resource array _R.array.selector_array_ is an array of strings to show in the dialog and preSelectedValue is the index to select on open.
Finally, your FragmentActivity will implement OnDialogSelectorListener and will receive the callback message.
public class MyActivity extends FragmentActivity implements OnDialogSelectorListener {
// ....
public void onSelectedOption(int selectedIndex) {
// do something with the newly selected index
}
}
I hope this is helpful to someone, as it took me MANY attempts to understand it. A full implementation of this type of DialogFragment with a callback is here.
public class SelectorDialog extends DialogFragment implements OnClickListener {
static final String TAG = "SelectorDialog";
static int mResourceArray;
static int mSelectedIndex;
static OnDialogSelectorListener mDialogSelectorCallback;
public interface OnDialogSelectorListener {
public void onSelectedOption(int dialogId);
}
public static DialogSelectorDialog newInstance(int res, int selected) {
final DialogSelectorDialog dialog = new DialogSelectorDialog();
mResourceArray = res;
mSelectedIndex = selected;
return dialog;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mDialogSelectorCallback = (OnDialogSelectorListener)activity;
} catch (final ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnDialogSelectorListener");
}
}
public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
builder.setTitle(R.string.select);
builder.setSingleChoiceItems(mResourceArray, mSelectedIndex, this);
builder.setPositiveButton(R.string.ok, this);
builder.setNegativeButton(R.string.cancel, this);
return builder.create();
}
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case Dialog.BUTTON_NEGATIVE:
dialog.cancel();
break;
case Dialog.BUTTON_POSITIVE:
dialog.dismiss();
// message selected value to registered calbacks
mDialogSelectorCallback.onSelectedOption(mSelectedIndex);
break;
default: // choice selected click
mSelectedIndex = which;
break;
}
}
}
Question from a comment How to call this from a Fragment instead of an Activity.
First make a few changes to the DialogFragment.
Remove the onAttach event since that's not the easiest way in this scenario.
Add a new method to add a reference to the callback
public void setDialogSelectorListener (OnDialogSelectorListener listener) {
this.mListener = listener;
}
Implement the listener in your Fragment
public class MyFragment extends Fragment implements SelectorDialog.OnDialogSelectorListener {
// ....
public void onSelectedOption(int selectedIndex) {
// do something with the newly selected index
}
}
Now create a new instance and pass in a reference to the Fragment to use it.
final SelectorDialog sd = SelectorDialog.newInstance(R.array.selector_array, preSelectedValue);
// this is a reference to MyFragment
sd.setDialogSelectorListener(this);
// mActivity is just a reference to the activity attached to MyFragment
sd.show(this.mActivity.getSupportFragmentManager(), TAG);
final CharSequence[] choice = {"Choose from Gallery","Capture a photo"};
int from; //This must be declared as global !
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
alert.setTitle("Upload Photo");
alert.setSingleChoiceItems(choice, -1, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (choice[which] == "Choose from Gallery") {
from = 1;
} else if (choice[which] == "Capture a photo") {
from = 2;
}
}
});
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (from == 0) {
Toast.makeText(activity, "Select One Choice",
Toast.LENGTH_SHORT).show();
} else if (from == 1) {
// Your Code
} else if (from == 2) {
// Your Code
}
}
});
alert.show();
As others have pointed out, implementation 'com.google.android.material:material:1.0.0' it is more simply
Refere this material guide for more. https://material.io/develop/android/docs/getting-started/
CharSequence[] choices = {"Choice1", "Choice2", "Choice3"};
boolean[] choicesInitial = {false, true, false};
AlertDialog.Builder alertDialogBuilder = new MaterialAlertDialogBuilder(getContext())
.setTitle(title)
.setPositiveButton("Accept", null)
.setNeutralButton("Cancel", null)
.setMultiChoiceItems(choices, choicesInitial, new DialogInterface.OnMultiChoiceClickListener() {
#Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
}
});
alertDialogBuilder.show();
Try this.
final String[] fonts = {"Small", "Medium", "Large", "Huge"};
AlertDialog.Builder builder = new AlertDialog.Builder(TopicDetails.this);
builder.setTitle("Select a text size");
builder.setItems(fonts, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if ("Small".equals(fonts[which])) {
Toast.makeText(MainActivity.this,"you nailed it", Toast.LENGTH_SHORT).show();
}
else if ("Medium".equals(fonts[which])) {
Toast.makeText(MainActivity.this,"you cracked it", Toast.LENGTH_SHORT).show();
}
else if ("Large".equals(fonts[which])){
Toast.makeText(MainActivity.this,"you hacked it", Toast.LENGTH_SHORT).show();
}
else if ("Huge".equals(fonts[which])){
Toast.makeText(MainActivity.this,"you digged it", Toast.LENGTH_SHORT).show();
}
// the user clicked on colors[which]
}
});
builder.show();