Long Ranger Long Ranger - 3 months ago 59
Android Question

Custom XML attribute on android databinding

I have used AppCompatSpinner for my fragment and I want to use

setOnItemSelectedListener()
in my layout. I tried to use the tutorial section from here

https://developer.android.com/topic/libraries/data-binding/index.html?hl=en#custom_setters

but it does not provide a complete example to do the simple action. And I also look for the answer from here

android databinding in custom controls

and I still dun understand how to do it. I would like to have a complete example to do the simple custom binding with some attributes that's not existed in xml attribute but it is useful in the UI control

Here is my xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:apps="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>

<data>

<import type="android.view.View"/>

<variable
name="handler"
type="com.my.OldHandlerInterface"/>
</data>

<merge
tools:showIn="@layout/fragment_stock_replacement">


<android.support.v7.widget.CardView
android:id="@+id/exist_eqpt_card"
style="@style/sccardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.29"
android:visibility="@{oldObj.updateOld_mode ? View.VISIBLE : View.GONE}"
>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:orientation="vertical">

<android.support.v7.widget.AppCompatSpinner
android:id="@+id/spn_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/chk_installed"
apps:adapter="@{statusAdapter}"/>
</RelativeLayout>

</android.support.v7.widget.CardView>
<!--</LinearLayout>-->

</merge>
</layout>


Here is my fragment

public class ReplacementFragment extends QRScanFragment {
../
@BindingAdapter("app:setOnItemSelectedListener")
public static void setOnItemSelectedListener(AppCompatSpinner view, int pos) {
//do sth
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.binding, container, false);
String[] status = new String[]{"Spare", "Lost", "Damage", "Faulty"};
statusAdapter = new StatusAdapter(getActivity(), status);
binding.setHandler(new Handler());
View view = binding.getRoot();
AppCompatSpinner lAppCompatSpinner = (AppCompatSpinner) view.findViewById(R.id.spn_status);
lAppCompatSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
}
}
}

Answer

You don't need anything special to assign to the OnItemSelectedListener:

<android.support.v7.widget.AppCompatSpinner
    android:id="@+id/spn_status"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_below="@+id/chk_installed"
    android:onItemSelectedListener="@{myItemSelectedListener}"
    apps:adapter="@{statusAdapter}"/>

The above assumes a myItemSelectedListener variable in your layout of the type OnItemSelectedListener.

If you want to use only the onItemSelected or onNothingSelected, you can use the attribute in your layout already:

<android.support.v7.widget.AppCompatSpinner
    android:id="@+id/spn_status"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_below="@+id/chk_installed"
    android:onItemSelected="@{handler::onItemSelected}"
    apps:adapter="@{statusAdapter}"/>

This assumes a method on handler's class:

public class Handler {
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        //...
    }
}

You can also use a lambda expression:

android:onItemSelected="@{(p, v, pos, id) -> handler.onItemSelected(v, pos)}"

Here, handler's class has a method:

public class Handler {
    public void onItemSelected(View view, int position) {
        //...
    }
}

In all these cases, you must assign the handler or listener in the onCreateView, just as you're doing above with the binding.setHandler(...) call. You don't need to call lAppCompatSpinner.setOnItemSelectedListener(...) because it will be done as part of the binding.