Sergei Bubenshchikov
Sergei Bubenshchikov

Reputation: 5371

How to pass variable number of arguments to databinding adapter?

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

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.

Upvotes: 2

Views: 2241

Answers (2)

George Mount
George Mount

Reputation: 20926

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
    }
}

Upvotes: 1

yennsarah
yennsarah

Reputation: 5517

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.

Upvotes: 0

Related Questions