Don't close the DialogPreference onClick if the condition is not met
I need AutoCompliteTextView in PreferenceActivity, so I extended DialogPreference. My autocomplex expects the user (helping) to enter the country name. I'm fine if the user clicks Cancel or doesn't enter any values, however I would like the username to be entered before closing the dialog. I tried to override onClick as
@Override
public void onClick(DialogInterface dialog, int which) {
if (!validator.isValid(textView.toString())) {
onDialogClosed(false);
} else {
//do something here
super.onClick(dialog, which);
}
}
also with onDialogClosed
@Override
protected void onDialogClosed(boolean positiveResult) {
if (validator.isValid(textView.toString())) {
//do something here
super.onDialogClosed(positiveResult);
}
}
source to share
As soon as the user clicks on the dialog button, the dialog is closed and there is nothing you can do to stop it.
The only thing I can think of is call getDialog()
in DialogPreference
, assign it to AlertDialog
, then call getButton()
to retrieve your positive button, and disable it by enabling it later when the input is valid.
source to share
I also ran into the problem that Android does not provide a built-in way to validate the newly entered preference value BEFORE the preferences dialog is closed. Checking after closing the dialog (which it does boolean onPreferenceChange
) can only detect that the value is incorrect and the application should prevent it from being saved, but that seems pretty awkward. Imagine that the user made a typo, the new value is not saved, but the dialog is closed and the user is told that he / she must repeat the process from the beginning. This definitely needs to be fixed.
When it comes to a programming problem, it's best to provide code to solve. This is why I am posting an answer with a ready-made copy and paste solution. This follows the obvious idea from one of the above answers, while it doesn't deal with reflexes as another, provided piece of code suggests.
public class CustomEditTextPreference extends EditTextPreference
{
// if true, this preference requires new values to be checked for conformance to e-mail syntax
private boolean isEmail = false;
public CustomEditTextPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
// set isEmail either from custom XML-attributes (look up through attrs)
// or just by key
// if(getKey().equals(KNOWN_EMAIL_PREF))
// isEmail = true;
}
/**
* Checks if newValue conforms to a specific rule/syntax.
* Returns error code equal to resource ID of corresponding error message if the value is incorrect,
* or 0 if the validation was successful
*
* @param newValue a string with new preference value that needs a check-up
* @return integer error code equal to error message resource id
*/
private int isValid(String newValue)
{
int result = 0; // no error
if(isEmail)
{
if(!android.util.Patterns.EMAIL_ADDRESS.matcher(newValue).matches())
{
result = R.string.invalid_email;
}
}
// ...
// other check-ups if necessary
return result;
}
@Override
protected void showDialog(Bundle state)
{
super.showDialog(state);
final AlertDialog d = (AlertDialog)getDialog();
final EditText edit = getEditText();
d.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
int errorCode = isValid(edit.getText().toString());
Boolean canCloseDialog = (errorCode == 0);
if(canCloseDialog)
{
d.dismiss();
onDialogClosed(true);
}
else
{
String errorMessage = getContext().getString(errorCode);
Toast t = Toast.makeText(getContext(), errorMessage, Toast.LENGTH_LONG);
t.setGravity(Gravity.CENTER, 0, 0);
t.show();
}
}
});
}
}
I think the code pretty much explains itself. If the user fills in the field with the wrong email and clicks OK, the dialog remains open and the error message is displayed with a toast.
source to share
In fact, using reflection, I achieved what you said.
@Override
public void onClick(DialogInterface dialog, int which) {
if(!validate(arg)){
try {
// do not close
Field field = dialog.getClass().getSuperclass()
.getDeclaredField("mShowing");
field.setAccessible(true);
field.set(dialog, false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
source to share
If you want to show some error in the dialog instead of disabling the button, you need to create a CustomEditTextPreference class that extends the EditTextPreference
below is a code snippet
public class CustomEditTextPreference extends EditTextPreference {
EditText setPasswordEditText;
private Context context;
AlertDialog alertDialog;
public CustomEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
setPasswordEditText = this.getEditText();
}
@Override
protected void showDialog(Bundle state) {
super.showDialog(state);
alertDialog = (AlertDialog) getDialog();
alertDialog.setCanceledOnTouchOutside(false);
Button positiveButton = alertDialog
.getButton(AlertDialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String str = setPasswordEditText.getText().toString();
if (/*condition not met*/) {
showError();
} else {
alertDialog.dismiss();
}
}
});
}
source to share