在 androidx.recyclerview.widget.RecyclerView 上找不到参数类型为布尔值的属性 'app:fastScrollEnabled' 的 setter

Cannot find the setter for attribute 'app:fastScrollEnabled' with parameter type boolean on androidx.recyclerview.widget.RecyclerView

我已经尝试设置 ObservableField 或 String 值,但仍然无效。如果我只是设置一个静态的 true 或 false 值而不是 viewModel 引用,它就可以工作。

布局文件:

     <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
                name="viewModel"
                type="app.viewmodel.ViewModel"/>
    </data>
 <androidx.recyclerview.widget.RecyclerView
                        android:id="@+id/recyclerview"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:scrollbars="vertical"
                        app:fastScrollEnabled="@{viewModel.isUserAdmin}"
                        app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
                        app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
                        app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
                        app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
                        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</layout>

ViewModel:

class AppointmentsViewModel()
{
     val isUserAdmin: Boolean = sharedPreferencesRepo.isUserAdmin
}

RecyclerView 目前没有 public 代码设置快速滚动属性的方法。在撰写本文时,启用快速滚动的唯一方法是在布局中设置相关属性 XML。 RecyclerView 然后从其构造函数调用一个私有方法来设置它。

数据绑定库在生成其适配器代码时找不到 public 属性的 fastScrollEnabled 方法,这就是您收到该错误的原因。只是现在还不可能。

a request in the Issue Tracker to add the relevant functionality to RecyclerView, and even a comment there that points out the data binding limitation,但目前似乎不是高优先级。


如果您不介意一些额外的工作,我们实际上可以手动完成此操作,因为 RecyclerViewFastScroller 实现只是 ItemDecorationOnItemTouchListener.我们可以从库源复制 class 并在外部设置它,允许我们以编程方式启用和禁用快速滚动,从而通过数据绑定来处理它。

直接复制 the current FastScroller class 应该只需要进行一些明显的小修改(例如,更改 package、一些注释等),下面的示例假定您已经完成以原名复制,FastScroller.

有一个重要的补充是允许从 RecyclerView 中无错误地分离 FastScroller。 (事实上​​ ,它似乎还没有出现。)在 FastScrollerdestroyCallbacks() 方法中,添加对 mShowHideAnimator.cancel():

的调用
private void destroyCallbacks() {
    mRecyclerView.removeItemDecoration(this);
    mRecyclerView.removeOnItemTouchListener(this);
    mRecyclerView.removeOnScrollListener(mOnScrollListener);
    cancelHide();

    // ADD THIS
    mShowHideAnimator.cancel();
}

幸运的是,FastScroller 在实例化后基本上不需要进一步关注,并且由于这无论如何都是在替换 RecyclerView-内部功能,因此将每个 FastScroller 实例附加到它的 RecyclerView 作为标签,因此我们不必在其他任何地方跟踪这些实例。我们只需要在以后检索它们一次即可分离,这​​种安排非常适合数据绑定设置。

我们需要定义一个唯一的资源 ID 作为我们的标签键。可以放在res/values/文件夹下的任意资源文件中; /res/values/ids.xml 会很合适,如果你喜欢整洁。

<resources>
    <id name="recycler_view_fast_scroller_tag_key" />
</resources>

我们的 FastScroller 初始化代码1 本质上是对 RecyclerView 及其构造函数中初始化的重新排序,合并到 static 实用程序中方法:

static void initFastScroller(RecyclerView recyclerView) {
    Resources resources = recyclerView.getContext().getResources();
    Resources.Theme theme = recyclerView.getContext().getTheme();

    StateListDrawable verticalThumbDrawable = (StateListDrawable)
            ResourcesCompat.getDrawable(resources, R.drawable.thumb_drawable, theme);
    Drawable verticalTrackDrawable =
            ResourcesCompat.getDrawable(resources, R.drawable.line_drawable, theme);
    StateListDrawable horizontalThumbDrawable = (StateListDrawable)
            ResourcesCompat.getDrawable(resources, R.drawable.thumb_drawable, theme);
    Drawable horizontalTrackDrawable =
            ResourcesCompat.getDrawable(resources, R.drawable.line_drawable, theme);

    if (verticalThumbDrawable == null || verticalTrackDrawable == null
            || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
        throw new IllegalArgumentException(
                "Trying to set fast scroller without all required drawables.");
    }

    FastScroller fastScroller =
            new FastScroller(recyclerView, verticalThumbDrawable, verticalTrackDrawable,
                    horizontalThumbDrawable, horizontalTrackDrawable,
                    resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
                    resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
                    resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));

    recyclerView.setTag(R.id.recycler_view_fast_scroller_tag_key, fastScroller);
}

为简单起见,此示例将所有特定资源选择移至 initFastScroller() 方法中,但当然可以根据需要修改和重新排列;例如,进入自定义 RecyclerView subclass。此外,您可以在此处指定您喜欢的任何维度参数,尽管该示例使用 RecyclerView 包中的默认资源值。2

drawables现在是动态处理的,所以需要从布局中移除它们对应的属性设置XML:3

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical"
    app:fastScrollEnabled="@{viewModel.isUserAdmin}"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

最后,为了将所有这些结合在一起,setFastScrollEnabled() 方法展示了我们如何以编程方式启用和禁用快速滚动,我们将其注释为 BindingAdapter 以将该功能公开给数据通过 fastScrollEnabled 属性绑定:

@BindingAdapter("fastScrollEnabled")
public static void setFastScrollEnabled(RecyclerView recyclerView, boolean setEnabled) {
    Object fastScroller = recyclerView.getTag(R.id.recycler_view_fast_scroller_tag_key);
    boolean isEnabled = fastScroller instanceof FastScroller;
    if (setEnabled != isEnabled) {
        if (setEnabled) {
            initFastScroller(recyclerView);
        } else {
            // Detach the FastScroller from the RecyclerView
            ((FastScroller) fastScroller).attachToRecyclerView(null);
            // Nullify our only reference to it
            recyclerView.setTag(R.id.recycler_view_fast_scroller_tag_key, null);
        }
    }
}

此方法首先在 R.id.recycler_view_fast_scroller_tag_keyRecyclerView 上检查我们的 FastScroller 标签。如果它在那里,则当前启用快速滚动,否则禁用。如果该状态与我们被调用设置的状态不同,则继续初始化和 setTag() FastScroller(它处理在实例化时将自身附加到 RecyclerView 内部),或者分离它并使标签无效。


1 Java 中提供这些方法仅仅是因为这是从原始来源借用的代码语言。如果您更愿意在 Kotlin 中使用它,Android Studio 可以作为第一步自动转换它,但您可能希望进一步 syntax/structural/etc。调整。

2 给定的三个 R.dimen 在我的测试中都没有问题地解决和构建,有点出乎意料。如果您确实想使用这些默认值但遇到问题,您也可以复制相应的 <dimen>s from the library source

3 这应该可以在 fastScrollEnabled 属性上有或没有 app 命名空间前缀,因为它显然是 ignored/stripped 由数据绑定框架。它留在XML这里只是为了显示必要的最小变化。


当然,这一切也独立于数据绑定工作,并且它可以改编成更合适的实用方法,或 Kotlin 扩展,例如:

var RecyclerView.isFastScrollEnabled: Boolean
    get() = getTag(R.id.recycler_view_fast_scroller_tag_key) is FastScroller
    set(value) {
        if (value != isFastScrollEnabled) {
            if (value) {
                // Convert initFastScroller() from above as you like
                initFastScroller(this)
            } else {
                // Detach the FastScroller from the RecyclerView
                (getTag(R.id.recycler_view_fast_scroller_tag_key) as FastScroller)
                    .attachToRecyclerView(null)
                // Nullify our only reference to it
                setTag(R.id.recycler_view_fast_scroller_tag_key, null)
            }
        }
    }