How to pass variable number of arguments to databi

2019-07-18 05:18发布

问题:

I have Spinner and some count of EditText at layout. I want to bind type of EditText to state of Spinner. I.e.

  • for FLOAT state of Spinner input type of all EditText components must be changed to floating point numbers
  • for INTEGER input type must be integer

To achive this I define binding adapter below:

@BindingAdapter(value = {"bind:selectedValueAttrChanged", "bind:relatedInputs"}, requireAll = false)
public static void setMeasurementUnit(final Spinner spinner, final InverseBindingListener listener, final AppCompatEditText... relatedInputs){
    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            listener.onChange();
            String selectedUnit = (String) spinner.getSelectedItem();

            for (EditText editText : relatedInputs) {
                if (editText == null) continue;

                if (selectedUnit.equals("INTEGER")) 
                    editText.setInputType(InputType.TYPE_CLASS_NUMBER);
                else if(selectedUnit.equals("FLOAT"))
                    editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {
            listener.onChange();
        }
    });
}

And bind EditText to Spinner at layout file:

<LinearLayout>
    <android.support.v7.widget.AppCompatEditText
         android:id="@+id/edit_text_1"/>

    <android.support.v7.widget.AppCompatSpinner
         bind:relatedInputs="@{editText1}"/>
</LinearLayout>

It's produce the error at compile time:

data binding error ****msg:Cannot find the setter for attribute 'bind:relatedInputs' with parameter type android.support.v7.widget.AppCompatEditText on android.support.v7.widget.AppCompatSpinner.

When I tried declare and pass array of EditText

<android.support.v7.widget.AppCompatSpinner
         bind:relatedInputs="@{new AppCompatEditText[]{editText1, editText2}}"/>

I'm got syntax error:

data binding error ****msg:Syntax error: extraneous input 'AppCompatEditText' expecting {<EOF>, ',', '.', '::', '[', '+', '-', '*', '/', '%', '<<', '>>>', '>>', '<=', '>=', '>', '<', 'instanceof', '==', '!=', '&', '^', '|', '&&', '||', '?', '??'}

How I can pass variable number of arguments to databinding adapter?

or

How to declare array for databinding at layout file?

P.S. this example very simplified and can contain logical errors, but it fully explain, what I want to achive.

回答1:

Android Data Binding doesn't let you pass in varargs. You may have only have one value for each attribute. You may pass lists or arrays as attribute values, but you can't create them on the fly (new is not allowed in the expression language).

It is better to invert the problem and assign the android:inputType attribute to an expression based on the selected item.

<LinearLayout>
    <android.support.v7.widget.AppCompatEditText
         android:inputType="@{Converters.getInputType(spinner1.selectedItem)}"/>

    <android.support.v7.widget.AppCompatSpinner
         android:id="@+id/spinner1"/>
</LinearLayout>

You'll need the Converters class also:

public class Converters {
    public static int getInputType(Object obj) {
        if (!(obj instanceof String)) {
            return InputType.TYPE_CLASS_TEXT; // Don't know what to do
        }

        String selectedUnit = (String) obj;

        if (selectedUnit.equals("INTEGER")) {
            return InputType.TYPE_CLASS_NUMBER;
        } else if(selectedUnit.equals("FLOAT")) {
            return InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL;
        }
        return InputType.TYPE_CLASS_TEXT; // Don't know what to do
    }
}


回答2:

Another approach would be:

//create a Observable for the input type:
public final ObservableInt decOrInt = new ObservableInt(InputType.TYPE_NUMBER_FLAG_DECIMAL);

Add a listener to your spinner:

binding.spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            decOrInt.set(i != 0 ? 
                    InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL : 
                        InputType.TYPE_CLASS_NUMBER);
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {
            decOrInt.set(InputType.TYPE_CLASS_NUMBER);
        }
    });

And in your xml, for every EditText you want:

<!-- act would be the class where you store your decOrInt-->
<EditText
        android:id="@+id/edit_text"
        android:inputType="@{act.decOrInt}" /> 

A decimal input corresponds to InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL, an integer input corresponds to InputType.TYPE_CLASS_NUMBER.

It is possible to enter a float and change the selection to int - you'll need to add an error or something to prevent this.