如何强制RecyclerView滚动,即使没有足够的项目填充屏幕高度

7
我在NavigationView中创建了一个RecyclerView,并使用自定义的第一项作为ListView的addHeaderView()的等效项。在标题布局的底部,我放置了一个EditText,以便过滤下面的列表。
我的想法是每当EditText获得焦点时,将RecyclerView向上滚动,直到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的代码片段(典型设置):
<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适配器。


试过这个吗?它是不同的效果,但结果相同。https://dev59.com/318d5IYBdhLWcg3wpzr4 - Q2x13
已经看过了。我的标题栏里有控件(包括可展开布局),所以视差效果会破坏它。更不用说使用库来实现这个效果会过度杀伤,因为我已经成功地在我的RecyclerView中实现了自定义标题栏。问题只是滚动位置。 - leondzn
尝试将RecyclerView的高度更改为“wrap_content”。 - Q2x13
这并没有帮助,因为我的目标是让RecyclerView(或其他控件)填充下面的空白区域。将其设置为wrap_content只会消除任何多余的空间,并且如果我的列表足够小,则会阻止进一步滚动。 - leondzn
1个回答

1
您需要的是一个页脚与页眉相同高度。
这里有两种情况需要考虑:
1. 您的页眉高度固定。 2. 您的页眉高度动态变化。
在第一种情况下,解决方案非常简单。只需定义您的页眉高度为某个值,例如<dimen name="header_height">300dp</dimen>,然后按以下方式定义您的页眉和页脚即可:
<FrameLayout
    android:layout_height="@dimen/header_height">

    <!-- Content goes here -->

</FrameLayout>

在第二种情况下,我们需要做一些事情:
  • 每当标题的高度更改时检索其高度
  • 将页脚的高度更新为标题高度的任何值
有很多方法可以做到这一点。我以前使用的一种方法是,在RecyclerView上使用ViewTreeObserver.OnPreDrawListener来检测标题何时具有高度(以及随后是否已更改),然后将该高度传递给您的RecyclerView.Adapter,该适配器将使用它来设置请求的高度,以便在充气页脚时,然后您只需要调用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 会破坏列表在向下移动时的动画效果。我进行了一些调整。为页脚添加了一个空视图,我称之为空格。我将其大小设置为recyclerVIew.getHeight() - (mItemHeight * (getItemCount()-2))以便在列表项不足以填满屏幕时始终填充屏幕(header 高度无关紧要)。我每次更新列表时都设置 spacer 的高度。感谢您指引我正确的方向。 :) - leondzn

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