RecyclerView快速滚动拇指高度太小,不适合大型数据集

时间:2017-12-16 14:53:40

标签: android android-recyclerview

我正在使用默认的SELECT p.product_id FROM oc_category_path cp LEFT JOIN oc_product_to_category p2c ON (cp.category_id = p2c.category_id) LEFT JOIN oc_product_filter pf ON (p2c.product_id = pf.product_id) LEFT JOIN oc_product p ON (pf.product_id = p.product_id) LEFT JOIN oc_product_description pd ON (p.product_id = pd.product_id) LEFT JOIN oc_product_to_store p2s ON (p.product_id = p2s.product_id) WHERE pd.language_id = 2 AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = 0 AND cp.path_id = 86125 AND pf.filter_id in(1901,1855) GROUP BY p.product_id ORDER BY p.price DESC, p.sort_order 快速滚动,我跟着this guide来支持它。

现在,问题是拇指根据数据集的大小调整其高度。对于100以上的大件物品,拇指变得非常小,几乎难以对拖动作出反应。

请问有什么方法可以为快速滚动拇指设置最小高度。

5 个答案:

答案 0 :(得分:3)

这只是部分答案;我错过了(至少)一块拼图,但希望其他人可以解决这个问题。

一旦您为<RecyclerView>标记添加了必要的属性(如在OP&#39的问题中链接的答案中所述),滚动条拇指的大小/位置由三种方法控制在LinearLayoutManager内:

  • int computeVerticalScrollRange():滚动条的音轨大小。

  • int computeVerticalScrollExtent():滚动条的大小。

  • int computeVerticalScrollOffset():滚动条轨道顶部与滚动条拇指顶部之间的距离。

这些方法的单位是任意的;只要所有三种方法共享相同的单位,你就可以使用任何你喜欢的东西。默认情况下,LinearLayoutManager将使用两组单位之一:

  • mSmoothScrollbarEnabled == true:根据RecyclerView中可见项目的像素大小使用单位。

  • mSmoothScrollbarEnabled == false:根据RecyclerView适配器中可见项的位置使用单位。

要自己控制滚动条拇指的大小,您必须覆盖这些方法......但这里是我错过的部分:在所有在我的实验中,computeVerticalScrollExtent()从未被系统调用。也就是说,我们仍然可以在这里取得一些进展。

首先,我创建了一个简单的适配器,显示500 CardView个内置项目的位置。我已经启用了一些非常简单(但丑陋)的drawables快速滚动。这里只是一个默认的LinearLayoutManager实现滚动条的样子:

enter image description here

正如您所发现的,500(小)项目,滚动条拇指非常小,很难点击。我们可以通过覆盖computeVerticalScrollRange()来使滚动条显着变大,只返回一个固定的常量......我基本上随机选择5000只是为了显示主要变化:

enter image description here

当然,现在滚动条并不像你期望的那样工作;滚动列表通过拖动它,正常情况下移动拇指的次数远远超过它应该的位置,通过拖动拇指快速滚动列表会使列表移动得比它应该少得多。

在我的设备上,使用随机选择的5000范围,覆盖computeVerticalScrollOffset(),如下所示,当我通过拖动列表滚动列表时,滚动条拇指移动完美:

@Override
public int computeVerticalScrollRange(RecyclerView.State state) {
    return 5000;
}

@Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
    return (int) (super.computeVerticalScrollOffset(state) / 23.5f);
}

然而,这仍然无法解决第二个问题:拖动拇指本身并不能正确滚动列表。正如我上面提到的,这里似乎适当的做法是覆盖computeVerticalScrollExtent(),但系统从不调用此方法。我甚至覆盖它只是抛出异常,我的应用程序永远不会崩溃。

希望这至少有助于指导人们朝着正确的方向寻找完整的解决方案。

PS:本回答中包含的computeVerticalScrollRange()computeVerticalScrollOffset()的实现是故意简单的(读:虚假)。 &#34;真&#34;实现会复杂得多;默认的LinearLayoutManager实现会考虑设备方向,列表中的第一个和最后一个可见项,两个方向的屏幕外项目数,平滑滚动,各种布局标志等。

答案 1 :(得分:2)

我通过复制Fast Scroller类解决了这个问题 android.support.v7.widget.FastScroller

然后我删除了从xml启用的快速滚动并使用以下代码应用了fastscroller

StateListDrawable verticalThumbDrawable = (StateListDrawable) getResources().getDrawable(R.drawable.fastscroll_sunnah);
Drawable verticalTrackDrawable = getResources().getDrawable(R.drawable.fastscroll_line_drawable);
StateListDrawable horizontalThumbDrawable = (StateListDrawable)getResources().getDrawable(R.drawable.fastscroll_sunnah);
Drawable horizontalTrackDrawable = getResources().getDrawable(R.drawable.fastscroll_line_drawable);

Resources resources = getContext().getResources();
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));

在FastScroller类中,我扩展了defaultWidth

FastScroller(RecyclerView recyclerView, StateListDrawable verticalThumbDrawable,
        Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
        Drawable horizontalTrackDrawable, int defaultWidth, int scrollbarMinimumRange,
        int margin) {
    ...
    this.defaultWidth = defaultWidth;
    ...

然后我更新了这个方法中的代码

void updateScrollPosition(int offsetX, int offsetY) {
...
mVerticalThumbHeight = Math.max(defaultWidth * 4, Math.min(verticalVisibleLength,
                (verticalVisibleLength * verticalVisibleLength) / verticalContentLength));
...
...
mHorizontalThumbWidth = Math.max(defaultWidth * 4, Math.min(horizontalVisibleLength,
                (horizontalVisibleLength * horizontalVisibleLength) / horizontalContentLength));
...    
}

这可确保最小拇指高度/宽度是默认宽度的4倍

答案 2 :(得分:1)

这是一个已知问题,于2017年8月开放:https://issuetracker.google.com/issues/64729576

仍在等待有关如何通过快速滚动大量数据来管理RecyclerView的建议。到目前为止,Google没有就此问题就建议做出回复:(

Nabil的答案类似于该问题中提到的解决方法。 Nabil的答案会复制FastScroller类并对其进行修改以确保最小尺寸,并且问题中的解决方法会延伸FastScroller(但必须保留在android.support.v7.widget包中)以确保最小拇指尺寸。

答案 3 :(得分:1)

Nabil Mosharraf Hossain's answer解决了此问题。我只想发布一些详细信息,以备将来参考。

默认情况下,不是在LinearLayoutManager中调用computeVerticalScrollExtent(),而是在updateScrollPosition方法的FastScroller类中计算了拇指的大小和拇指的位置。

这是拇指大小的公式:

this.mVerticalThumbHeight =
Math.min(verticalVisibleLength, verticalVisibleLength * verticalVisibleLength / verticalContentLength);

verticalVisibleLength是屏幕上可见的回收商内容的长度。 verticalContentLength是回收商整个内容的长度。因此,拇指在屏幕上所占的比例与可见内容在整个内容中所占的比例相同。这意味着如果回收者的内容很长-拇指会很小。

为防止这种情况,我们可以按照Nabil Mosharraf Hossain的建议改写mVerticalThumbHeight。我是这样做的,所以拇指仍然具有相同的比例大小,但不能低于100像素:

this.mVerticalThumbHeight = Math.max(100,
Math.min(verticalVisibleLength, verticalVisibleLength * verticalVisibleLength / verticalContentLength));

但是。仅覆盖拇指大小会导致屏幕顶部和底部附近的拇指移动出现一些不一致,因为它的位置会像拇指仍然很小一样表现相同的方式。为了防止这种情况,我们还可以覆盖拇指位置。

拇指位置的默认公式不考虑拇指大小,而只是使用与屏幕大小成比例的事实。因此,我发现此公式有效。但是我不记得它到底是做什么的。

this.mVerticalThumbCenterY = (int)((verticalVisibleLength - mVerticalThumbHeight) * offsetY
/ ((float)verticalContentLength - verticalVisibleLength) + mVerticalThumbHeight / 2.0);

答案 4 :(得分:0)

我找到了一种调整滚动条拇指大小的解决方案,但是我还没有完全解决这个问题。就像the answer of Ben P.一样,我使用了默认属性app:fastScrollEnabled和所有轨迹和拇指可绘制属性,并创建了自定义LinearLayoutManager

    class FastScrollLayoutManager(context: Context) : LinearLayoutManager(context) {

        override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State?): Int {
            return super.scrollVerticallyBy(dy * 2, recycler, state)
        }

        override fun computeVerticalScrollRange(state: RecyclerView.State): Int {
            return super.computeVerticalScrollRange(state) / 2
        }

        override fun computeVerticalScrollOffset(state: RecyclerView.State): Int {
            return super.computeVerticalScrollOffset(state) / 2
        }
    }

通过将computeVerticalScrollRange()computeVerticalScrollOfsett()的结果除以2,滚动指针将变大2倍。然后,如果将滚动速度乘以2,则滚动速度将再次在正确的比例上。拖动滚动条的拇指将非常正常,并且拇指的大小将比以前大2倍。

但是,这还不是最终解决方案。通过提高滚动速度,“正常”滚动(不使用滚动条)将不再平滑。