Android ViewPager 的事件侦听器数据绑定
Android Event Listener Data Binding for ViewPager
是否可以使用 Android 绑定功能将 setOnPageChangeListener 的处理程序绑定到 XML 文件中的 ViewPager?
演示显示了 onClick 事件,但我很好奇我可以用它实现多少事件功能。数据绑定功能的任何链接也很好。谢谢
假设示例:
example_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="handlers" type="com.example.Handlers"/>
</data>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onPageChangeListener="@{handlers.pageChanged}" />
</layout>
Handler.java
package com.example.viewmodels;
import android.view.View;
public class Handlers {
public void pageChanged(View view){}
}
编译错误为:
错误:(62) 在包 'android'
中找不到属性 'onPageChangeListener' 的资源标识符
这是可以做到的。您需要实施自定义绑定适配器,因为 Android 支持库中没有为 View class 预定义的 BindingAdapter classes。
关于如何实现自定义适配器,您可以阅读 this。
代码应该像下面这样,我还没有彻底测试它们:
<android.support.v4.view.ViewPager
android:id="@+id/pager"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:onPageChangeListener="@{handlers}" />
您的 BindingAapter 代码:
@BindingAdapter("onPageChangeListener")
public static void setOnPageChangeListener(ViewPager viewPager, ViewPager.OnPageChangeListener listener) {
Log.i("setOnPageChangeListener");
// clear listeners first avoid adding duplicate listener upon calling notify update related code
viewPager.clearOnPageChangeListeners();
viewPager.addOnPageChangeListener(listener);
}
P.S。您传递的处理程序 class 应该实现 ViewPager.OnPageChangeListener
.
这是最终的解决方案。只需将此 class 放入您的项目中即可。
@BindingMethods({
@BindingMethod(type = ViewPager.class, attribute = "android:offscreenPageLimit", method = "setOffscreenPageLimit"),
@BindingMethod(type = ViewPager.class, attribute = "android:adapter", method = "setAdapter"),
@BindingMethod(type = ViewPager.class, attribute = "android:currentPage", method = "setCurrentItem"),
})
public final class ViewPagerBindingAdapter {
@InverseBindingAdapter(attribute = "android:currentPage", event = "android:currentPageAttrChanged")
public static int getCurrentPage(@NonNull final ViewPager pager) {
return pager.getCurrentItem();
}
@BindingAdapter(value = {"android:onPageScrolled", "android:onPageSelected", "android:onPageScrollStateChanged",
"android:currentPageAttrChanged"}, requireAll = false)
public static void onSetAdapter(@NonNull final ViewPager pager, final OnPageScrolled scrolled, final OnPageSelected selected,
final OnPageScrollStateChanged scrollStateChanged, final InverseBindingListener currentPageAttrChanged) {
final ViewPager.OnPageChangeListener newValue;
if (scrolled == null && selected == null && scrollStateChanged == null && currentPageAttrChanged == null) {
newValue = null;
} else {
newValue = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
if (scrolled != null) {
scrolled.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(final int position) {
if (selected != null) {
selected.onPageSelected(position);
}
if (currentPageAttrChanged != null) {
currentPageAttrChanged.onChange();
}
}
@Override
public void onPageScrollStateChanged(final int state) {
if (scrollStateChanged != null) {
scrollStateChanged.onPageScrollStateChanged(state);
}
}
};
}
final ViewPager.OnPageChangeListener oldValue = ListenerUtil.trackListener(pager, newValue, R.id.page_change_listener);
if (oldValue != null) {
pager.removeOnPageChangeListener(oldValue);
}
if (newValue != null) {
pager.addOnPageChangeListener(newValue);
}
}
public interface OnPageScrolled {
void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
}
public interface OnPageSelected {
void onPageSelected(int position);
}
public interface OnPageScrollStateChanged {
void onPageScrollStateChanged(int state);
}
private ViewPagerBindingAdapter() {
throw new UnsupportedOperationException();
}
}
同时在你的资源中添加id资源。
<resources>
<item name="page_change_listener" type="id"/>
</resources>
然后您就可以在 xml 中使用它,例如:
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:currentPage="@={viewModel.currentPage}"
android:offscreenPageLimit="@{viewModel.offscreenPageLimit}"
android:onPageSelected="@{currentPage -> viewModel.pageSelected(currentPage)}"
android:adapter="@{adapter}"/>
如您所见,currentPage
具有反向绑定,因此您的 viewModel 将能够设置当前页面并在用户滑动时获取当前页面。
您可以使用 SimpleOnPageChangeListener
而不是 OnPageChangeListener
,并且只覆盖您感兴趣的方法:
@BindingAdapter("onPageChanged")
@JvmStatic
fun addPageChangedListener(viewPager: ViewPager, listener: OnPageChanged) {
viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
listener.onPageChanged(position)
}
})
}
interface OnPageChanged {
fun onPageChanged(position: Int)
}
布局:
<androidx.viewpager.widget.ViewPager
app:onPageChanged="@{viewModel::handlePageChanged}">
处理程序:
fun handlePageChanged(position: Int) {
println("Page changed: $position")
}
正如其他人所指出的,您可能需要注意不要附加重复的侦听器。
是否可以使用 Android 绑定功能将 setOnPageChangeListener 的处理程序绑定到 XML 文件中的 ViewPager?
演示显示了 onClick 事件,但我很好奇我可以用它实现多少事件功能。数据绑定功能的任何链接也很好。谢谢
假设示例:
example_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="handlers" type="com.example.Handlers"/>
</data>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onPageChangeListener="@{handlers.pageChanged}" />
</layout>
Handler.java
package com.example.viewmodels;
import android.view.View;
public class Handlers {
public void pageChanged(View view){}
}
编译错误为:
错误:(62) 在包 'android'
中找不到属性 'onPageChangeListener' 的资源标识符这是可以做到的。您需要实施自定义绑定适配器,因为 Android 支持库中没有为 View class 预定义的 BindingAdapter classes。
关于如何实现自定义适配器,您可以阅读 this。
代码应该像下面这样,我还没有彻底测试它们:
<android.support.v4.view.ViewPager
android:id="@+id/pager"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:onPageChangeListener="@{handlers}" />
您的 BindingAapter 代码:
@BindingAdapter("onPageChangeListener")
public static void setOnPageChangeListener(ViewPager viewPager, ViewPager.OnPageChangeListener listener) {
Log.i("setOnPageChangeListener");
// clear listeners first avoid adding duplicate listener upon calling notify update related code
viewPager.clearOnPageChangeListeners();
viewPager.addOnPageChangeListener(listener);
}
P.S。您传递的处理程序 class 应该实现 ViewPager.OnPageChangeListener
.
这是最终的解决方案。只需将此 class 放入您的项目中即可。
@BindingMethods({
@BindingMethod(type = ViewPager.class, attribute = "android:offscreenPageLimit", method = "setOffscreenPageLimit"),
@BindingMethod(type = ViewPager.class, attribute = "android:adapter", method = "setAdapter"),
@BindingMethod(type = ViewPager.class, attribute = "android:currentPage", method = "setCurrentItem"),
})
public final class ViewPagerBindingAdapter {
@InverseBindingAdapter(attribute = "android:currentPage", event = "android:currentPageAttrChanged")
public static int getCurrentPage(@NonNull final ViewPager pager) {
return pager.getCurrentItem();
}
@BindingAdapter(value = {"android:onPageScrolled", "android:onPageSelected", "android:onPageScrollStateChanged",
"android:currentPageAttrChanged"}, requireAll = false)
public static void onSetAdapter(@NonNull final ViewPager pager, final OnPageScrolled scrolled, final OnPageSelected selected,
final OnPageScrollStateChanged scrollStateChanged, final InverseBindingListener currentPageAttrChanged) {
final ViewPager.OnPageChangeListener newValue;
if (scrolled == null && selected == null && scrollStateChanged == null && currentPageAttrChanged == null) {
newValue = null;
} else {
newValue = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
if (scrolled != null) {
scrolled.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(final int position) {
if (selected != null) {
selected.onPageSelected(position);
}
if (currentPageAttrChanged != null) {
currentPageAttrChanged.onChange();
}
}
@Override
public void onPageScrollStateChanged(final int state) {
if (scrollStateChanged != null) {
scrollStateChanged.onPageScrollStateChanged(state);
}
}
};
}
final ViewPager.OnPageChangeListener oldValue = ListenerUtil.trackListener(pager, newValue, R.id.page_change_listener);
if (oldValue != null) {
pager.removeOnPageChangeListener(oldValue);
}
if (newValue != null) {
pager.addOnPageChangeListener(newValue);
}
}
public interface OnPageScrolled {
void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
}
public interface OnPageSelected {
void onPageSelected(int position);
}
public interface OnPageScrollStateChanged {
void onPageScrollStateChanged(int state);
}
private ViewPagerBindingAdapter() {
throw new UnsupportedOperationException();
}
}
同时在你的资源中添加id资源。
<resources>
<item name="page_change_listener" type="id"/>
</resources>
然后您就可以在 xml 中使用它,例如:
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:currentPage="@={viewModel.currentPage}"
android:offscreenPageLimit="@{viewModel.offscreenPageLimit}"
android:onPageSelected="@{currentPage -> viewModel.pageSelected(currentPage)}"
android:adapter="@{adapter}"/>
如您所见,currentPage
具有反向绑定,因此您的 viewModel 将能够设置当前页面并在用户滑动时获取当前页面。
您可以使用 SimpleOnPageChangeListener
而不是 OnPageChangeListener
,并且只覆盖您感兴趣的方法:
@BindingAdapter("onPageChanged")
@JvmStatic
fun addPageChangedListener(viewPager: ViewPager, listener: OnPageChanged) {
viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
listener.onPageChanged(position)
}
})
}
interface OnPageChanged {
fun onPageChanged(position: Int)
}
布局:
<androidx.viewpager.widget.ViewPager
app:onPageChanged="@{viewModel::handlePageChanged}">
处理程序:
fun handlePageChanged(position: Int) {
println("Page changed: $position")
}
正如其他人所指出的,您可能需要注意不要附加重复的侦听器。