This question already has answers here:
Android Fragment handle back button press [duplicate]
(25 answers)
Closed 7 years ago.
I am new on android. I cant handle onBackPressed method. I have an Activity class which has four fragment like A,B,C, D. When i lunched Activity by default Fragment A is active and there are link on fragment A to move another Fragment. I want when move another Fragment like B,C,D from Fragment A and pressed Back button it return to Fragment A and if i pressed Back button from Fragment A it show a dialog box.
I used onBackPressed() like below
public void onBackPressed() {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
MainActivity.this);
// set title
alertDialogBuilder.setTitle("Exit");
// set dialog message
AlertDialog.Builder builder = alertDialogBuilder
.setMessage("Do you really want to exit?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, close
// current activity
MainActivity.this.finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
But it work on all fragment and i want to work only Fragment A
You have to handle such requirements in the Activity's onBackPressed only. I usually follow the following approach:
I keep an enum having all the fragments defined and a parameter to track the current fragment:
enum FRAGMENTS{
fragmentA, fragmentB, fragmentC, fragmentD
}
FRAGMENTS mCurrentFragment;
Now the logic where you change the fragment should have:
public void changeFragment(FRAGMENTS newFragment){
//Your logic
mCurrentFragment = newFragment;
}
And finally the logic onBackPressed:
onBackPressed{
if(mCurrentFragment == FRAGMENTS.fragmentA){
//Your code here of asking the user if he/she really wants to quit
super.onBackPressed();
}else{
changeFragment(getPreviousFragment(mCurrentFragment));
}
}
If you dont want to use enum, you can have final int or any other string values to represent different fragments and a mCurrentFragment parameter to keep a track of the currentFragment being shown and then you can easily play with the code in your Activity's onBackPressed method
Let me know if you need more clarification.
Add fragment to backStack while replacing
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
And then override onBackPressed method inside an Activity
So Here is your answer which you are looking for :)
Method 1
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// Your stuff here
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this);
// set title
alertDialogBuilder.setTitle("Exit");
// set dialog message
AlertDialog.Builder builder = alertDialogBuilder.setMessage("Do you really want to exit?")
.setCancelable(false).setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, close
// current activity
System.exit(0);
}
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
}
//Tell me if you face any issue
Method 2 or in your case just paste this method in your main activity so you can also achieve it what you want :)
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this);
// set title
alertDialogBuilder.setTitle("Exit");
// set dialog message
AlertDialog.Builder builder = alertDialogBuilder.setMessage("Do you really want to exit?").setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, close
// current activity
System.exit(0);
}
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
If you want to override the onBack pressed method then add this code in your in onActivityCreated() method
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
getFragmentManager().popBackStack();
return true;
}
return false;
}
});
Use the below code hope it helps.
//Always replace/add your fragment with a tag so that you can use that tag in future
Fragment fr = getSupportFragmentManager().findFragmentByTag("Fragment_Name");//Fragment Name is a tag to identify fragment
if (fr == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_container, new Demo_Fragment(), "Fragment_Name").commit();
}
//Now override onBackPressed method in MainActivity Only no need to do it in Fragment Class
#Override
public void onBackPressed() {
Fragment fr = getSupportFragmentManager().findFragmentByTag("FragmentA");
if(fr==null)
//replace FragmentA
else
//Show Alert Box
}
For that you need to do following
Create method on the BaseFragment or Activity that save the current fragment object
public void setCurrentFragment(Fragment currentFragment) {
this.currentFragment = currentFragment;
}
and also create a method that return your current fragment object
public Fragment getCurrentFragment() {
return currentFragment;
}
now just you need to call the getCurrentFragment Method and checked that if the Fragment is an instance of the A fragment or not
if (getCurrentFragment() instanceof AFragment) {
showAlert
} else {
super.onBackPressed()
}
Related
I am using navigation component, and when I use device back button or toolbar back arrow I want to show alert dialog or custom dialog before navigate to previous fragment, if user press yes then navigate to the previous fragment, anything else dismiss like a dialog functionality. I already tried this:
OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
#Override
public void handleOnBackPressed() {
// Handle the back button event
}
};
this is my code for fragment and actions and i asked to how to handle the backarrow event or callback in navigation component architecture so as i move to previous fragment so before going to back it will display a alert are you sure to move from here like that.
<fragment
android:id="#+id/drawOnImageFragment"
android:name="com.consulthealthcare.app.fragments.DrawOnImageFragment"
android:label="Mark on Prescription"
tools:layout="#layout/fragment_draw_on_image">
<argument
android:name="uri"
app:argType="string" />
<action
android:id="#+id/action_drawOnImageFragment_to_confirmOrderFragment"
app:destination="#id/confirmOrderFragment"
app:enterAnim="#anim/enter_from_right"
app:exitAnim="#anim/exit_to_left"
app:popEnterAnim="#anim/enter_from_left"
app:popExitAnim="#anim/exit_to_right" />
<action
android:id="#+id/action_drawOnImageFragment_to_dialogNavFragment"
app:destination="#id/dialogNavFragment" />
</fragment>
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
This worked for the device back button but not for the navigation component toolbar back arrow.
on back button you have to override the onbackpress method
and inside it you would write your custom alert dialog
something like this:
#Override
public void onBackPressed()
{
// Create the object of
// AlertDialog Builder class
AlertDialog.Builder builder
= new AlertDialog
.Builder(context/* requireActivity() in fragment*/ );
// Set the message show for the Alert time
builder.setMessage("Do you want to go back ?");
// Set Alert Title
builder.setTitle("Alert !");
// Set Cancelable false
// for when the user clicks on the outside
// the Dialog Box then it will remain show
builder.setCancelable(false);
// Set the positive button with yes name
// OnClickListener method is use of
// DialogInterface interface.
builder
.setPositiveButton(
"Yes",
new DialogInterface
.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which)
{
// write what you want to do here for yes
}
});
// Set the Negative button with No name
// OnClickListener method is use
// of DialogInterface interface.
builder
.setNegativeButton(
"No",
new DialogInterface
.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which)
{
// If user click no
// then dialog box is canceled.
dialog.cancel();
}
});
// Create the Alert dialog
AlertDialog alertDialog = builder.create();
// Show the Alert Dialog box
alertDialog.show();
}
and for back arrow same concept
I would suggest to write a method for back arrow and back button contains the alert and call it when these button pressed
finally i have done the navigation component toolbar back arrow using
binding.toolbar.setNavigationOnClickListener(this);
#Override
public void onClick(View v) {
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.navHostFragment);
Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);
if (fragment instanceof HostFragment) {
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START, true);
} else {
binding.drawerLayout.openDrawer(GravityCompat.START, true);
}
} else {
navController.navigateUp();
}
}
this line make the default functionality of the navigation
navController.navigateUp();
I am trying to include an AlertDialog builder within a method that prompts for a pin code and when the positive button is pressed, checks it against a database value and returns a true or false value to the method caller.
For example: Adding/editing/deleting a user task requires a pin code. I don't want to generate a different AlertDialog for all three (and more) of these actions. I want to wrap the following code within a TaskService class that I can call from any activity, and react based on the result from within that activity.
So TaskService.java would have:
public boolean isCorrectPin(View v){
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
final EditText editText = new EditText(context);
builder.setView(editText);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (editText.getText().toString()) == getPinCode(){
//return true
}
}
});
builder.show();
}
and OpenTaskAdapter.java would have:
public void onBindViewHolder(ViewHolder holder, int position){
holder.btnMarkAsComplete.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
if (service.isCorrectPin(v) {
//complete task
}
}
});
holder.btnDelete.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
if (service.isCorrectPin(v) {
//delete task
}
}
});
It's important to note that these two button listeners could be in totally different activities.
You can create your own method to generate dialog with listener:
public void isCorrectPin(Context context, String title, String message, String btnPositive, final DialogSingleButtonListener dialogSingleButtonListener) {
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
dialogBuilder.setTitle(title);
dialogBuilder.setMessage(message);
dialogBuilder.setPositiveButton(btnPositive, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (editText.getText().toString() == getPinCode()){
dialogSingleButtonListener.onButtonClicked(dialog);
}
}
});
AlertDialog dialog = dialogBuilder.create();
dialog.show();
}
And the listener class:
public interface DialogSingleButtonListener {
public abstract void onButtonClicked(DialogInterface dialog);
}
And use it like:
service.isCorrectPin(context, title, message, btnPositive
new DialogSingleButtonListener() {
#Override
public void onButtonClicked(DialogInterface dialog) {
//code here is only called if they entered a correct pin.
}
}
);
A dialog can't "return" a value in the way that it looks like you're expecting. A dialog can make changes to some other object, but you can't have a bit of code block on it and wait for the user to finish interacting with it.
Instead, you'll need to set up listeners for when the prompt dialog is dismissed or buttons or clicked, or whatever other event signals that you have what you need from it. Those listeners can then read the data gathered and set by the dialog.
this is how i'm doing :
public Boolean showAlert(String message)
{
action = false;
AlertDialog.Builder alertDialog = new AlertDialog.Builder(HAActivity.this);
// Setting Dialog Title
alertDialog.setTitle(getString(R.string.app_name));
// Setting Dialog Message
alertDialog.setMessage(message);
// Setting Icon to Dialog
// Setting Positive "Yes" Button
alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
// Write your code here to invoke YES event
action = true;
}
});
// Setting Negative "NO" Button
alertDialog.setNegativeButton("Cancle", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Write your code here to invoke NO event
action = false;
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
return action;
}
and calling function like this :
//activity in which you create function
if (Activity.showAlert("Do you really want to delete ??"))
{
//delete it anyway.
}
Let's say my first activity has the following code:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == SET_PLAN_REQUEST) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
fixedPlan = data.getParcelableExtra("fixedPlan");
recreate();
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crop_rotation_planner);
// Hide the action bar; I don't want to see that hideous thing, yet.
getSupportActionBar().hide();
// Initialize imageLoader
MainActivity.imageLoader = new ImageLoader(getApplicationContext());
restore(savedInstanceState);
}
private void restore(Bundle savedInstanceState) {
// Check if activity has been launched previously
if (savedInstanceState != null) {
// Get saved crop rotation plan
fixedPlan = savedInstanceState.getParcelable("fixedPlan");
// Enable or disable btnViewPlan
checkIfEnoughCropFamilies();
// Set number of crop families
setNumberOfCropFamilies();
// get mSoilTypesList
mSoilTypesList = (ArrayList<String>) savedInstanceState.getStringArrayList("mSoilTypesList");
selectedTypeOfSoil = savedInstanceState.getString("selectedTypeOfSoil");
mPlantList = savedInstanceState.getParcelableArrayList("mPlantList");
// if mSoilTypesList hasn't been acquired, then execute loadSoilTypesFromNet to fetch data
if (mSoilTypesList == null) {
loadSoilTypesFromNet.execute();
}
// else if mSoilTypesList already exists, just display the soil types
else {
// only display if user hasn't selected soil yet
if (selectedTypeOfSoil == null) {
displaySoilTypes();
}
// if user has already selected soil type, then display plants
else {
getPlants();
}
}
}
else {
loadSoilTypesFromNet.execute();
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putStringArrayList("mSoilTypesList", mSoilTypesList);
savedInstanceState.putParcelableArrayList("mPlantList", mPlantList);
savedInstanceState.putString("selectedTypeOfSoil", selectedTypeOfSoil);
savedInstanceState.putParcelable("fixedPlan", fixedPlan);
}
#Override
protected void onDestroy() {
dismissAlertDialog();
dismissProgressDialog();
loadPlantsFromNet.cancel(true);
loadSoilTypesFromNet.cancel(true);
super.onDestroy();
}
#Override
public void onBackPressed() {
if (!fixedPlan.isEmpty()) {
AlertDialog.Builder builder = new AlertDialog.Builder(CropRotationPlannerActivity.this);
builder.setTitle(R.string.str_dbox_exit_crp_title);
builder.setMessage(R.string.str_dbox_exit_crp_message);
builder.setPositiveButton(R.string.str_dbox_exit_crp_ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
finish();
CropRotationPlannerActivity.super.onBackPressed();
}
});
builder.setNegativeButton(R.string.str_dbox_exit_crp_cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alert = builder.create();
alert.setCanceledOnTouchOutside(false);
alert.setCancelable(false);
alert.show();
}
else
super.onBackPressed();
}
And my proceeding activity has the following important code:
// We'll be sending our fixedPlan back to previous activity (CropRotationPlannerActivity)
#Override
public void onBackPressed() {
// TODO :: THINK OF THE SAVE FEATURE
final Intent intent = new Intent();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.str_dbox_exit_title);
builder.setMessage(R.string.str_dbox_exit_message);
builder.setPositiveButton(R.string.str_dbox_exit_yes, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
intent.putExtra("fixedPlan", fixedPlan);
setResult(RESULT_OK, intent);
// TODO : Somewhat buggy code here?
// TODO : Takes quite sometime to process :/
finish();
PlanActivity.super.onBackPressed();
}
});
builder.setNegativeButton(R.string.str_dbox_exit_no, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
setResult(RESULT_CANCELED, intent);
dialog.dismiss();
finish();
PlanActivity.super.onBackPressed();
}
});
builder.setNeutralButton(R.string.str_dbox_exit_cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.setCancelable(false); // so as to prevent the back button from closing the dialog box
alert = builder.create();
alert.setCanceledOnTouchOutside(false); // prevents user from canceling dialog box by clicking outside
alert.show();
}
When the user exits the first activity and the Object fixedPlan doesn't have any contents, I want the user to exit to the main menu. When it doesn't have any contents, I want to pop a DialogBox so that the user is aware that the current activity contains important info and ask the user one more time if he really wants to exit.
To get to the next activity, fixedPlan should not be empty. And getting out from that activity, fixedPlan is never empty, too. But why does the back button not respond once the user goes back to the previous activity? What should I do?
NOTE THAT THIS ONLY HAPPENS WHEN positive button is pressed ( in the second activity). Another thing is that once I have pressed the positive button on the second activity, and the moment I'm in the first activity, and then I go back there and press the negative button, the back button works. It's really an issue with the positive button. BTW, I can feel the back button's vibration when pressed. LOL. Just that. It doesn't respond.
Adding finish() does not work either (to the else statement in my onBackPressed method for the first activity).
Try changing adding dialog.dismiss() before finish() in Positive button click event inside the second activity?
Also, I believe you should remove the calls to PlanActivity.super.onBackPressed();, both under positive and negative buttons click.
I can't figure out why pressing the hardware back button (or the dialog OK button for that matter) doesn't return me to the initial DialogFragment at times.
I have a DeductionListDialog fragment that is called from the options menu of MainActivity as such:
public boolean onOptionsItemSelected(MenuItem item) {
...
case R.id.action_deduction_list:
DialogFragment newFragment = new DeductionListDialog();
newFragment.show(getFragmentManager(), "dialog");
return true;
default:
return super.onOptionsItemSelected(item);
}
DeductionListDialog then has a couple of onClickListeners within its onCreateDialog method:
// the listview that holds the deduction list
ListView listview = (ListView) view.findViewById(android.R.id.list);
listview.setOnItemClickListener(new OnItemClickListener() {
#Override
// set a short click listener
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// create a new dialog fragment
DialogFragment deduction_specifics = new DeductionSpecificsDialog();
// / bundle database row so we can get the correct info
// for our specific listing
Bundle arguments = new Bundle();
arguments.putLong("database_row", id);
deduction_specifics.setArguments(arguments);
deduction_specifics.show(getFragmentManager(), "dialog");
}
});
// set the long click listener
listview.setOnItemLongClickListener(new OnItemLongClickListener() {
// on long click we want to open the edit fragment
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
DialogFragment deduction_edit = new DeductionEditFragment();
Bundle arguments = new Bundle();
arguments.putLong("database_id", id);
deduction_edit.setArguments(arguments);
deduction_edit.show(getFragmentManager(), "dialog");
/*Intent deduction_edit_intent = new Intent(getActivity(), DeductionEditActivity.class);
deduction_edit_intent.putExtra("database_id", id);
startActivity(deduction_edit_intent);*/
return true;
}
});
When the listview onItemClick & onItemLongClick listeners are clicked, another dialog pops up with various pieces of information. When I press the back button, or the dialog's OK button, I return to the initial DeductionListDialog fragment as expected.
The DeductionListDialog also has within its onCreateDialog method:
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
...
alertDialogBuilder.setView(view);
alertDialogBuilder.setTitle("Deductions: ");
alertDialogBuilder.setMessage("Long press to update or delete");
alertDialogBuilder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User clicked OK button
dialog.dismiss();
}
});
alertDialogBuilder.setNegativeButton("Add", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User clicked Add button
DialogFragment deduction_edit = new DeductionEditFragment();
Bundle arguments = new Bundle();
deduction_edit.setArguments(arguments);
deduction_edit.show(getFragmentManager(), "dialog");
/*Intent deduction = new Intent(getActivity(), DeductionEditActivity.class);
startActivity(deduction);*/
}
});
When I click the "Add" button, a new DeductionEditFragment is created. When I click back (or the dialog's cancel & accept buttons) I expect the view to go back to the original DeductionListDialog fragment, but clicks simply result in the fragment closing back to the MainActivity.
1) Why is this the case as I'd like to learn how to prevent this in the future.
2) What's the quickest way to resolve this issue?
3) What's the 'proper' way of resolving this issue if differenct from #2?
A pastebin of the entire three classes (DeductionListDialog, DeductionEditDialog, DeductionSpecificsDialog) can be found here: http://pastebin.com/AJQ6KCEN
Thanks all.
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();