Question

I have need for AutoCompliteTextView in PreferenceActivity so I extended DialogPreference. My auto-complite is expecting from (helping) user to enter country name. I'm fine if user press Cancel or enters no values, however I would like to ensure that proper name is entered before dialog is closed. 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);
        }
    }
Was it helpful?

Solution

Once a user clicks on a dialog button, the dialog is closing, and there is nothing you can do to stop it.

The only thing I can think of that you could try is to call getDialog() on the DialogPreference, cast that to AlertDialog, then call getButton() to retrieve your positive button, and disable it, enabling it later when the input is valid.

OTHER TIPS

I also faced the problem, that Android does not provide a built-in way to check up a newly entered preference value BEFORE the preference dialog is closed. A check up performed after the dialog is closed (what the boolean onPreferenceChange accomplishes), can only discover that the value is incorrect and the app should prevent it from being saved, but this seems rather inconvenient. Just imagine that a user made a typo, new value is not saved, but the dialog is closed, and the user is informed that he/she must repeat the process from very beginning. It surely should be fixed.

When it comes to a problem in programming, it's better to provide a code for solution. This is why I'm posting the answer with the solution ready for copy&paste. It follows the obvious idea from one of the answers above, whereas it does not deal with reflections as the other provided code-snippet implies.

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 is pretty much self-explaining. If a user fills in the field with incorrect e-mail, and presses OK button, then the dialog remains open and an error message is shown via toast.

Actually, by 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();
        }
    }
}

If you want to show some error in a dialog instead of disabling the button then you need to create a class CustomEditTextPreference that extends EditTextPreference

below is 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();
            }

        }
    });

}

You can override the onPrepareDialogBuilder() inside the DialogPreference

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top