android - 将 CalendarView 与数据绑定(bind)结合使用

标签 android kotlin android-databinding android-livedata calendarview

我想对 Android LiveData 组件使用双向数据绑定(bind) ( as an alternative for Observable fields 。以下是包含 CalendarViewEditText 的简单项目的代码显示有关单击按钮的信息。

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

和 Activity 代码:

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

双向绑定(bind)适用于字符串,但不适用于日期。我发现this post ,它表示所选日期实际上与 android:date 使用的日期不同...很公平,您可以在监听器中捕获更改日期的操作。问题在于,当存在双向绑定(bind)设置时,监听器 setOnDateChangeListener(在上面的 also {} 中应用)根本不会被触发。

如果我错了,请纠正我,但如果我想获取所选日期我必须使用OnDateChangeListener。它似乎也与 android:date@={...} 不兼容,因为使用双向绑定(bind)似乎会覆盖我们的监听器。如果 android:date@={...} 提供与 OnDateChangeListener 相同的功能,那就有意义了,但事实并非如此。

所以最后一个问题是:是否可以通过双向数据绑定(bind)以某种方式获取所选日期?

最佳答案

有很多错误,是我在跟踪所有类后发现的。

错误 1

这是 Android 文档的错误。请参阅CalendarViewBindingAdapter类。

您可以看到他们为 android:date 创建了绑定(bind)适配器,但没有 @InverseBindingAdapter

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

// no @InverseBindingAdapter written

但是在 documentation ,他们写道 CalendarView 支持双向绑定(bind)。

也许我们会在下次更新中得到这个。

我还尝试添加@InverseBindingAdapter,但这也不起作用。

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

错误 2

尝试在 CalendarView 上设置 setOnDateChangeListener,您将始终获得相同的日期。

以下不起作用

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

以下作品

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

这就是我的 @InverseBindingAdapter 不起作用的原因。

因为 calendarView.getDate() 没有给出正确的日期。

修复

您可以通过创建适配器来解决此问题,直到他们无法解决此问题为止。只需将下面的类放入您的项目中,一切都会顺利进行。

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();
                }
            });
        }
    }
}

我修复了什么

我刚刚将日期设置为 CalendarView (view.setDate()),之前为 0。

关于android - 将 CalendarView 与数据绑定(bind)结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52389235/

相关文章:

kotlin - 每当 Recyclerview 行中的文本在 Kotlin 中更改时,是否有办法更改 MainActivity 中的 textview 文本

android - 启用 safe-args 插件后无法从数据绑定(bind)获取 Root View

android - 通过Wifi Direct连接多个Android设备

kotlin - 如何用协程实现非阻塞

android - kotlin 中的重载解析歧义错误

android - 如何根据列表是否为空更改 View 的可见性

android - 数据绑定(bind)中调用方法

java - 线程 hibernate ,不做

android - 使用 Android NDK 将文件写入 SDcard 以外的位置?

android - 为什么我的 Xamarin Forms 应用程序位于 Android 4.4 - API 19 (Samsung Galaxy S3) 中的所有其他应用程序之上?