4

I am trying to do some validation of an editText field, allow me to elaborate because I am using a bunch of concepts that all have their own questions in stackOverflow but I have the feeling that what I'm trying to do is not yet answered in any of them, and this is something rather basic so either I'm screwing up badly or I just found a bug, also, many of the popular questions and answers are a couple of years old...

I have two Edittext fields which contain an initial date and a termination date which are input from a datepicker, I am validating that after the termination date has been input by the datepicker this date happens after the initial date, and that if it doesn't it displays an error using setError.

So far so good, I manage to do all that but there is something bothering me:

When I first click on the EditText it does nothing (because I use setInputType(InputType.TYPE_NULL) along with android:focusableInTouchMode="true" in the XML; to avoid showing me the keyboard, so it clicks, get selected, and no keyboard, but also no datepicker, until you click again, then the onClick method triggers the datepicker and all works as intended.)

I was going to show you images but I don't have the rep points :/

If I use setFocusable(false), something weird happens, when the setError comes up only the icon shows without the text.

If I don't use setFocusable(false), the editText appears as selected when the activity starts (no cursor flashing because I disabled input) and stays selected (it stays blue but you can click anywhere)

If I don't want it to be selected at start I use android:focusableInTouchMode="true" in the XML in the parent layout, per recommendation of this question (Stop EditText from gaining focus at Activity startup) and that solves that problem. But I have the first click and nothing happens second click and shows the datepicker problem again... what am I doing wrong.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:background="#FFFFFF"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    style="@style/AppTheme"
    android:id="@+id/fechasalariolayout"
    android:focusableInTouchMode="true">

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textAlignment="center"
    android:inputType="date"
    android:ems="10"
    android:gravity="center_horizontal"
    android:onClick="showDatePickerDialogFT"
    android:id="@+id/fechaterminacioninpt"
    android:layout_weight="10"
    android:layout_gravity="center_horizontal" />

public class fechaSalario extends ActionBarActivity {

    private Button calcularbtn;
    static EditText fechaingresoinpt, fechaterminacioninpt, salarioinpt;
    public static boolean isFechaInicial, isFechaTerminacion = false;
    private RadioGroup sueldoPeriodo, salarioMin;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Remove title bar
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);

        //Remove notification bar
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        //set content view AFTER ABOVE sequence (to avoid crash)
        this.setContentView(R.layout.fechasalario);
        calcularbtn = (Button) findViewById(R.id.calcularbtn);
        fechaingresoinpt = (EditText) findViewById(R.id.fechaingresoinpt);
        fechaterminacioninpt = (EditText) findViewById(R.id.fechaterminacioninpt);
        salarioinpt = (EditText) findViewById(R.id.salarioinpt);
        sueldoPeriodo = (RadioGroup)findViewById(R.id.sueldoperiodo);
        salarioMin = (RadioGroup) findViewById(R.id.salariomin);
        fechaterminacioninpt.setInputType(InputType.TYPE_NULL);
        //fechaterminacioninpt.setFocusable(false);


        //OnChangeTxtListener
        fechaterminacioninpt.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence s, int start, int before,
                                      int count) {
                // TODO Auto-generated method stub
            }
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count,
                                          int after) {
                // TODO Auto-generated method stub
            }
            @Override
            public void afterTextChanged(Editable s) {
                // TODO Auto-generated method stub
                fechaterminacioninpt.setError(null);
                validarFechaTerminacion(fechaterminacioninpt, fechaingresoinpt); // pass your EditText Obj here.
            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        return super.onOptionsItemSelected(item);
    }

    public void onClickCalcular(View view) {
        //Llamar a una funcion calcular que llame a un activity para desplegar.
        // Enviar BUNDLE

        //Verificar que los datos hayan sido introducidos correctamente.
        //verificar fechas y cantidades válidas.
        //verificar que todos los radio buttons esten seleccionados


        Intent intent = new Intent(this, calcular.class);
        Bundle variables = new Bundle();
        variables = getIntent().getExtras();
        RadioButton sueldoperiodo =  (RadioButton)this.findViewById(sueldoPeriodo.getCheckedRadioButtonId());
        RadioButton salariomin =  (RadioButton)this.findViewById(salarioMin.getCheckedRadioButtonId());

        //validar Fecha inicial menor a fecha de terminación

        variables.putString("FECHA_INICIO", String.valueOf(fechaingresoinpt.getText()));
        variables.putString("FECHA_TERMINACION", String.valueOf(fechaterminacioninpt.getText()));
        variables.putString("SALARIO", String.valueOf(salarioinpt.getText()));
        variables.putString("SUELDO_PERIODO", sueldoperiodo.getText().toString());
        variables.putString("SALARIO_MIN", salariomin.getText().toString());
        intent.putExtras(variables);
        startActivity(intent);
    }

    public void validarFechaTerminacion(EditText edt, EditText edt2){

        DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy");
        DateTime fechaInicio = formatter.parseDateTime(String.valueOf(fechaingresoinpt.getText()));
        DateTime fechaTerm = formatter.parseDateTime(String.valueOf(fechaterminacioninpt.getText()));

        if (fechaInicio.isAfter(fechaTerm)){
           /* fechaterminacioninpt.requestFocus();*/
            fechaterminacioninpt.setError("La fecha de terminación ocurre antes que la fecha de inicio");
        }
        else{
            // Do nothing
            fechaterminacioninpt.setOnFocusChangeListener(new View.OnFocusChangeListener() {

                @Override
                 public void onFocusChange(View v, boolean hasFocus){

                    fechaterminacioninpt.setError(null);

                }
            });

        }

        //validar cantidad es un numero monetario válido

        //validar cantidad es un numero entero válido

        // validar radio buttons han sido seleccionados.

    }

    public void showDatePickerDialogFI(View v) {
        isFechaInicial = true;
        isFechaTerminacion = false;
        DialogFragment newFragment = new DatePickerFragment();
        newFragment.show(getFragmentManager(), "datePicker");
    }

    public void showDatePickerDialogFT(View v) {
        isFechaTerminacion = true;
        isFechaInicial = false;
        DialogFragment newFragment = new DatePickerFragment();
        newFragment.show(getFragmentManager(), "datePicker");
    }

}
Community
  • 1
  • 1

1 Answers1

1

I have a similar "problem" concerning the error message and focusability. If the textView is not focusable the message isn't shown. I suppose this is how it works e.g. if you have many fields which are validated and you get a validation error in each of them, the message is shown only for the textView which contains the cursor.

In an app I'm using the time and date picker the way you do. I have textViews which are validated but not focusable. My layout looks like this:

<LinearLayout
    android:id="@+id/time"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    android:layout_marginTop="10dp"
    android:background="@drawable/box_bg"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingRight="13dp"
        android:text="@string/start_time" />

    <EditText
        android:id="@+id/editText_start_time"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginRight="10dp"
        android:layout_weight="1"
        android:focusable="false"
        android:inputType="none"
        android:onClick="showTimePicker" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="13dp"
        android:text="@string/end_time" />

    <EditText
        android:id="@+id/editText_end_time"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginRight="10dp"
        android:layout_weight="1"
        android:focusable="false"
        android:inputType="none"
        android:onClick="showTimePicker" />
</LinearLayout>

Maybe this will solve your problem with the textViews getting the focus automatically. If not, let us know and we will figure something out.

[EDIT] Here is another code snippet I'm using:

<TableRow
    android:id="@+id/tablerow"
    android:layout_marginBottom="3dp"
    android:layout_marginTop="3dp"
    android:background="@drawable/box_bg"
    android:baselineAligned="false">

    <TextView
        android:id="@+id/text_view_birthday"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_column="0"
        android:layout_gravity="center_vertical"
        android:layout_span="1"
        android:text="@string/birthday" />

    <EditText
        android:id="@+id/text_birthday"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_column="1"
        android:layout_gravity="center_vertical"
        android:cursorVisible="false"
        android:editable="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:inputType="date|textNoSuggestions"
        android:layout_weight="1"
        android:layout_span="2" />
</TableRow>
Kumiho
  • 121
  • 4
  • do you mean textviews or editTexts? if I use android:focusable="false" as you describe I end up with the "setError with icon but no text" problem, the same as if I use setFocusable(false), which is the exact same I think, – Antonio Garcia May 14 '15 at 21:25
  • Sorry, I mean editTexts but I think, this sould by applicable to textViews as well. Yes, this is the result one should expect. You can't focus the editText/textView that's why you can't see the message. All you can see is just the icon. I suppose this is the correct behavior for a not focusable view. – Kumiho May 14 '15 at 21:34
  • I'm not sure that is normal behaviour, because it wouldn't show the icon either, but if that is the case, then, how can it stay focusable while avoiding the problem of not triggering the datePicker until you make a second click? – Antonio Garcia May 14 '15 at 21:38
  • I had the same problem but unfortunately I'm not sure how I solved it :(. Does it show the cursor if you click for the first time on the view? If not, then I suppose some other view is getting the focus. Maybe you could give it a try to implement a custom [focusChangeListener](http://developer.android.com/reference/android/view/View.OnFocusChangeListener.html) or a custom [clickListener](http://developer.android.com/reference/android/view/View.OnClickListener.html). – Kumiho May 14 '15 at 22:25
  • It doesn't show the cursor because of the setInputType(InputType.TYPE_NULL), but otherwise you might be right about the focus being elsewhere. let me look deeper into this. thanks :) – Antonio Garcia May 15 '15 at 02:41
  • How about you declare all attributes in your XML-file instead of doing it programmatically. set `android:inputType="none"` , `android:focusable="false"` for your editText and `android:focusableInTouchMode="false"` for your parent layout. I suppose your layout is getting the focus right now. I hope this won't make the editText getting the focus automatically. But since it's not focusable this shouldn't happen. – Kumiho May 15 '15 at 08:42
  • I came up with a solution that fixes the problem as well, you must use android:focusable="false" in the xml to avoid having focus when the activity starts, and you will need android:inputType="none" to disable the keyboard when it is pressed. This will prevent you from showing your seterror correctly as it will not be focusable and will not display the text, but something I didn't know is that you can override the focusable=false programatically, by requesting focus and using textfield.setFocusableInTouchMode(true); gaining again focus and being able to display set error properly. – Antonio Garcia May 15 '15 at 12:02
  • I will make an example and post is as an answer as there are many independent questions addressing all of this aspects separately, I want to establish something like what is being discussed here: http://ux.stackexchange.com/questions/78609/what-is-the-best-way-to-handle-a-form-with-a-start-date-and-end-date – Antonio Garcia May 15 '15 at 12:06