【问题标题】:How to force RecyclerView to scroll when there's not enough items to fill the height of the screen当没有足够的项目来填充屏幕的高度时如何强制 RecyclerView 滚动
【发布时间】:2017-02-22 04:05:13
【问题描述】:

我在 NavigationView 中创建了一个 RecyclerView,其中包含一个自定义的第一项,相当于 ListView 的 addHeaderView()。在该标题布局的底部,我放置了一个 EditText,以便我可以过滤掉下面的列表。

我想要做的是将 RecyclerView 一直向上滚动,直到 EditText 成为最顶部的项目,只要 EditText 获得焦点。但是,如果我的项目数量足够小以至于无法填满 NavigationView 的整个高度,则 RecyclerView 拒绝一直向上滚动,从而显示除 EditText 之外的更多标题。另外一个问题是,即使列表足够大,一旦我开始过滤列表项,它仍然会向下滚动。

如何强制我的 RecyclerView 仍然一直向上滚动,即使我没有足够的项目来填充我的视图的高度,这样我的标题的其余部分就不会被偷看?

这是我想要实现的目标的可视化:

 _______________
| HEADER LINE 1 |
| HEADER LINE 2 |
| HEADER LINE 3 |
| ------------- |
|  Search box   |
| ------------- |
| List item 1   |
| List item 2   |
| List item 3   |
 ---------------

当我专注于搜索框时的预期行为:

 _______________
| ------------- |
|  Search box   | <- becomes the topmost item. I can already achieve this
| ------------- |    with RecyclerView.smoothScrollBy(dx, dy)
| List item 1   |    except when the list is small enough (see below)
| List item 2   |
| List item 3   |
| List item 4   |
|               | <- empty space is okay and is desired if dataset
|               |    is small enough
 ---------------

实际发生的情况:

 _______________
| HEADER LINE 2 |  <- the header is 'bleeding' on top
| HEADER LINE 3 |
| ------------- |
|  Search box   |  <- search box cannot take the topmost spot
| ------------- |     as the recycler tries to position itself to fill in
| List item 1   |     the bottom part with items
| List item 2   |
| List item 3   |
| List item 4   |  <- items are pushed down to the bottom to fill
 ---------------      in the space

这是我的 NavigationView xml 的 sn-p(只是您的典型设置):

<android.support.design.widget.NavigationView
    android:id="@+id/navigation_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/drawer_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</android.support.design.widget.NavigationView>

此处带有自定义标头的 ViewHolder(RecyclerView 适配器):

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if(viewType == HEADER) {
        return new HeaderHolder(mHeaderView);
    } else {
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item, parent, false);

        return new ItemViewHolder(v);
    }
}

抽屉标题只是我在主要活动中膨胀的一个 xml 布局(我在那里分配了侦听器和动画)并通过我制作的 setHeaderView(View header) 传递给我的 RecyclerView 适配器。

【问题讨论】:

  • 试过这个吗?这是不同的效果相同的结果。 stackoverflow.com/questions/26568087/…
  • 已经看到了。我的标题中有控件(包括可扩展布局),所以视差效果会毁掉它。更不用说使用库了,因为我已经在我的 RecyclerView 中成功实现了一个自定义标头。问题只是滚动位置
  • 尝试将回收站视图的高度更改为wrap_content
  • 这无济于事,因为我的目标是让 RecyclerView (或其他)填充下面的空白。将其设置为 wrap_content 只会消除任何多余的空格,并且如果我的列表足够小,则会阻止进一步滚动。

标签: android android-layout android-recyclerview


【解决方案1】:

您需要一个与页眉高度相同的页脚。

这里有两种情况需要考虑:

  1. 您的标题具有固定高度
  2. 您的标题具有动态高度

在第一种情况下,解决方案非常简单。只需用 &lt;dimen name="header_height"&gt;300dp&lt;/dimen&gt; 之类的值定义页眉的高度,然后像下面这样定义页眉和页脚:

<FrameLayout
    android:layout_height="@dimen/header_height">

    <!-- Content goes here -->

</FrameLayout>

在第二种情况下,我们将不得不做几件事:

  • 在标题发生变化时检索标题的高度
  • 将页脚高度更新为页眉高度的任何值

有很多方法可以做到这一点。我以前使用过的一种方法是,您可以在 RecyclerView 上使用 ViewTreeObserver.OnPreDrawListener 来检测标题何时具有高度(以及随后是否已更改),然后将该高度传递给您的 RecyclerView.Adapter 将使用它为页脚充气时设置请求的高度,然后您只需要在适配器上调用notifyItemChanged(int) 并使用页脚的索引告诉它重新充气您的页脚。以下是我刚刚在代码中描述的大部分内容:

final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.drawer_content);
recyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        if(recyclerView.getChildCount() < 1 || recyclerView.getChildAt(0) == null || recyclerView.getChildAt(0).getHeight() == 0) {
            return true;
        }

        int headerHeight = recyclerView.getChildAt(0).getHeight();
        adapter.setFooterHeight(headerHeight);

        return true;
    }
});

在你的适配器中:

int footerHeight = 0;

public void setFooterHeight(int footerHeight){
    if(this.footerHeight == footerHeight){
        return;
    }

    this.footerHeight = footerHeight;
    notifyItemChanged(getItemCount() - 1);
}

然后您唯一需要做的另一件事就是膨胀您的页脚,就像您已经是您的页眉一样,并将其高度设置为footerHeight

希望这会有所帮助。

【讨论】:

  • 这几乎是准确的。我有一个动态调整大小的标题。设置页脚高度,然后 notifyItemChanged 会在列表向下移动时弄乱列表的动画。我做了一些调整。对页脚有一个空视图,我称之为 spacer。我将它的大小设置为recyclerVIew.getHeight() - (mItemHeight * (getItemCount()-2)),以便在列表项不足以填满屏幕的情况下始终填满屏幕(标题高度无关紧要)。每次更新列表时,我都会设置间隔高度。感谢您为我指明正确的方向。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多