Using CalendarView with databinding

☆樱花仙子☆ 提交于 2019-12-01 08:13:25

问题


I want to use two-way databinding with Android LiveData components (as an alternative for Observable fields. Here's code for simple project with CalendarView and EditText that displays both info on button clicked.

<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
    <variable
        name="testDate"
        type="android.arch.lifecycle.MutableLiveData&lt;Long&gt;" />
    <variable
        name="testString"
        type="android.arch.lifecycle.MutableLiveData&lt;String&gt;" />
</data>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onButtonClick"
        android:text="Show data"/>

    <CalendarView
        android:id="@+id/cal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:date="@={testDate}"/>

    <EditText
        android:id="@+id/str"
        android:text="@={testString}"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>
</layout>

And activity code:

class MainActivity : AppCompatActivity() {

val liveDate = MutableLiveData<Long>().apply { value = System.currentTimeMillis() }
val liveString = MutableLiveData<String>().apply { value = "Date: " }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
            .also {
                it.testDate = liveDate
                it.testString = liveString

                it.cal.setOnDateChangeListener { view, year, month, dayOfMonth ->
                    Toast.makeText(applicationContext, "${view.date}", Toast.LENGTH_SHORT).show()
                }

            }
}

@TargetApi(Build.VERSION_CODES.O)
fun onButtonClick(view: View) {
    Toast.makeText(this, liveString.value +
            Instant.ofEpochMilli(liveDate.value!!).atZone(ZoneId.systemDefault()).toLocalDate()
            , Toast.LENGTH_SHORT)
            .show()
}
}

Two-way binding works fine for String, but not for the date. I found this post, which says that selected date is actually something different from the date used by android:date... Fair enough, you can capture this action of changing the date in the listener. The problem is that listener setOnDateChangeListener (applied in also {} above) is not triggered at all, when there is a two-way binding setup.

Correct me if I'm wrong, but if I want to get the selected date I have to use OnDateChangeListener. It also appears to be incompatible with android:date@={...} because using two-way binding seems to override our listener. It would make sense if android:date@={...} provided the same functionality as OnDateChangeListener, but it does not.

So the final question is: is it possible to get the selected date somehow with two-way databinding?


回答1:


There are bunch of bugs, which I found after tracking all classes.

Bug 1

This is bug of Android Documentation. See CalendarViewBindingAdapter class.

You can see they have created binding adapter for android:date, but there is no @InverseBindingAdapter.

@BindingAdapter({"android:date"})
public static void setDate(CalendarView view, long date) {
    if (view.getDate() != date) {
        view.setDate(date);
    }
}

// no @InverseBindingAdapter written

But on documentation, they have written that CalendarView supports two-way binding.

Perhaps we will get this in next updates.

I also tried to add @InverseBindingAdapter but that was not working too.

@InverseBindingAdapter(attribute = "android:date", event = "android:dateAttrChanged")
public static long getDateLong(CalendarView view) {
    return view.getDate();
}

Bug 2

Try setting setOnDateChangeListener on CalendarView, you will get same date always.

Below does not work

binding.cal.setOnDateChangeListener((view, year, month, dayOfMonth) -> {
     Log.d(TAG, "aLong: " + new Date(view.getDate()).toString());
});

Below works

binding.cal.setOnDateChangeListener((view, year, month, dayOfMonth) -> {
    Log.d(TAG, "aLong: " + new Date(year, month, dayOfMonth).toString());
});

That's why my @InverseBindingAdapter does not work.

Because calendarView.getDate() is not giving correct date.

Fix

You can fix this by creating your adapter till they don't fix this issue. Just put below class in your project, and everything will work well.

public class CalendarViewBindingAdapter {
    @BindingAdapter(value = {"android:onSelectedDayChange", "android:dateAttrChanged"},
            requireAll = false)
    public static void setListeners(CalendarView view, final CalendarView.OnDateChangeListener onDayChange,
                                    final InverseBindingListener attrChange) {
        if (attrChange == null) {
            view.setOnDateChangeListener(onDayChange);
        } else {
            view.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
                @Override
                public void onSelectedDayChange(CalendarView view, int year, int month,
                                                int dayOfMonth) {
                    if (onDayChange != null) {
                        onDayChange.onSelectedDayChange(view, year, month, dayOfMonth);
                    }
                    Calendar instance = Calendar.getInstance();
                    instance.set(year, month, dayOfMonth);
                    view.setDate(instance.getTimeInMillis());
                    attrChange.onChange();
                }
            });
        }
    }
}

What I fixed

I just set date to CalendarView (view.setDate()), which was 0 previously.



来源:https://stackoverflow.com/questions/52389235/using-calendarview-with-databinding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!