将 CalendarView 与数据绑定结合使用
Using CalendarView with databinding
我想使用带有 Android LiveData
组件的双向数据绑定(as an alternative for Observable fields。这是带有 CalendarView
和 EditText
的简单项目的代码显示有关单击按钮的信息。
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="testDate"
type="android.arch.lifecycle.MutableLiveData<Long>" />
<variable
name="testString"
type="android.arch.lifecycle.MutableLiveData<String>" />
</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()
}
}
双向绑定适用于字符串,但不适用于日期。我发现 ,它说选择的日期实际上与 android:date
使用的日期不同......很公平,你可以在监听器中捕获这个更改日期的动作。问题是当存在双向绑定设置时,根本不会触发侦听器 setOnDateChangeListener
(在上面的 also {}
中应用)。
如果我错了请纠正我,但如果我想获得选定的日期我必须使用OnDateChangeListener
。它还似乎与 android:date@={...}
不兼容,因为使用双向绑定似乎覆盖了我们的侦听器。如果 android:date@={...}
提供与 OnDateChangeListener
相同的功能,那将是有意义的,但事实并非如此。
所以最后一个问题是:是否可以通过双向数据绑定以某种方式获取所选日期?
有一堆错误,我在跟踪所有 classes 后发现的。
错误 1
这是 Android 文档的错误。参见 CalendarViewBindingAdapter class。
您可以看到他们已经为 android:date
创建了绑定适配器,但是没有 @InverseBindingAdapter
。
@BindingAdapter({"android:date"})
public static void setDate(CalendarView view, long date) {
if (view.getDate() != date) {
view.setDate(date);
}
}
// no @InverseBindingAdapter written
但是在documentation上,他们写道CalendarView
支持双向绑定。
也许我们会在下次更新中得到这个。
我也尝试添加 @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()
没有给出正确的日期。
修复
您可以通过创建适配器来解决此问题,直到他们无法解决此问题为止。只要在你的项目中放在class下面,一切都会很好。
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 LiveData
组件的双向数据绑定(as an alternative for Observable fields。这是带有 CalendarView
和 EditText
的简单项目的代码显示有关单击按钮的信息。
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="testDate"
type="android.arch.lifecycle.MutableLiveData<Long>" />
<variable
name="testString"
type="android.arch.lifecycle.MutableLiveData<String>" />
</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()
}
}
双向绑定适用于字符串,但不适用于日期。我发现 android:date
使用的日期不同......很公平,你可以在监听器中捕获这个更改日期的动作。问题是当存在双向绑定设置时,根本不会触发侦听器 setOnDateChangeListener
(在上面的 also {}
中应用)。
如果我错了请纠正我,但如果我想获得选定的日期我必须使用OnDateChangeListener
。它还似乎与 android:date@={...}
不兼容,因为使用双向绑定似乎覆盖了我们的侦听器。如果 android:date@={...}
提供与 OnDateChangeListener
相同的功能,那将是有意义的,但事实并非如此。
所以最后一个问题是:是否可以通过双向数据绑定以某种方式获取所选日期?
有一堆错误,我在跟踪所有 classes 后发现的。
错误 1
这是 Android 文档的错误。参见 CalendarViewBindingAdapter class。
您可以看到他们已经为 android:date
创建了绑定适配器,但是没有 @InverseBindingAdapter
。
@BindingAdapter({"android:date"})
public static void setDate(CalendarView view, long date) {
if (view.getDate() != date) {
view.setDate(date);
}
}
// no @InverseBindingAdapter written
但是在documentation上,他们写道CalendarView
支持双向绑定。
也许我们会在下次更新中得到这个。
我也尝试添加 @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()
没有给出正确的日期。
修复
您可以通过创建适配器来解决此问题,直到他们无法解决此问题为止。只要在你的项目中放在class下面,一切都会很好。
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。