Android中ViewPager的事件监听器数据绑定

12

是否可以使用Android Binding功能将setOnPageChangeListener的处理程序绑定到XML文件中的ViewPager?演示显示了onClick事件,但我想知道可以实现多少事件功能。如果有关Data Binding功能的能力的链接也会很好。谢谢。

假设性示例:

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'的资源标识符。
3个回答

11

这里是终极解决方案。只需将此类放入您的项目中即可。

@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将能够设置当前页面并在用户滑动时获取当前页面。


这些设置能否动态地实时更新ViewPager中的片段? - Azizi Musa
@azizi-musa,你可以将viewModel.currentPage的值设置为选择另一页。如果你想要更改ViewPager中的片段,你应该在PagerAdapter中进行操作。 - b1n0m
ListenerUtil.trackListener的目的是什么,以及添加和删除监听器的作用是什么? - Big McLargeHuge
如果有人调用invalidateAll()notifyChange(),它会尝试添加另一个监听器。因此,它用于排除可能意外添加多个监听器的情况。 - b1n0m

11

可以实现这一点。您需要实现自定义绑定适配器,因为Android支持库中的View类没有预定义的BindingAdapter类。

关于如何实现自定义适配器,您可以阅读此文档

代码应该类似于下面的示例,但我尚未对它们进行全面测试:

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

备注:传递的处理程序类应该实现ViewPager.OnPageChangeListener接口。


2
不是好的解决方案。因为ViewPager将OnPageChangeListener添加到内部ArrayList中,调用notifyChange()会使其添加多个侦听器。 - b1n0m
@b1n0m 更新了代码,先移除监听器再添加监听器。 - chubao
@David Cheung,当我调试应用程序时,我的监听器返回null。你有什么想法为什么会发生这种情况吗? - Siddhivinayak
@DavidCheung 如果我有一个 PageIndicator.setPager(viewpager),我该如何将 viewpager 绑定到 indicator - mr.icetea
@David Cheung,我也有一个问题,因为当我添加这段代码时,圆形页面指示器不显示当前页面。如何将其与viewpager连接起来? - Zoran

2

不必使用 OnPageChangeListener,可以使用 SimpleOnPageChangeListener,并只覆盖您感兴趣的方法:

@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")
}

正如其他人所指出的,您可能希望小心不要附加重复的监听器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接