NestedScrollView内嵌RecyclerView时滚动到指定位置失效问题
解决NestedScrollView嵌套RecyclerView时滚动到指定位置失效的问题
我太懂这个坑了!当你把RecyclerView塞进NestedScrollView里时,RecyclerView自带的滚动方法(比如startSmoothScroll)大概率会失效,核心原因有两个:
- 你给RecyclerView设置了
android:layout_height="wrap_content",这会让RecyclerView把所有item一次性渲染出来,它自身没有可滚动的空间——毕竟内容高度和自身高度完全一致,滚动操作自然没意义。 - NestedScrollView会接管整个布局的滚动事件,RecyclerView的滚动请求会被它拦截掉。
下面给你两个可行的解决方案,按需选择:
方案一:保留NestedScrollView,通过计算偏移量实现滚动
如果你的列表项数量不多,非要用嵌套结构,可以通过计算目标item在NestedScrollView中的位置,让NestedScrollView滚动到对应坐标:
// 确保RecyclerView完成布局后再执行滚动 recyclerView.post(new Runnable() { @Override public void run() { // 获取目标位置的item视图 View targetItemView = recyclerView.getLayoutManager().findViewByPosition(pos); if (targetItemView != null) { // 计算该视图相对于NestedScrollView的顶部偏移量 int[] viewLocation = new int[2]; targetItemView.getLocationInWindow(viewLocation); int scrollOffset = viewLocation[1] - nestedScrollView.getTop(); // 让NestedScrollView平滑滚动到目标位置 nestedScrollView.smoothScrollTo(0, scrollOffset); } } });
另外记得把RecyclerView上多余的app:layout_behavior="@string/appbar_scrolling_view_behavior"去掉——这个behavior只需要给外层的NestedScrollView设置就够了。
方案二:移除NestedScrollView,将头部布局转为RecyclerView的Header(推荐)
如果你的列表项数量较多,强烈不推荐嵌套NestedScrollView——这会完全废掉RecyclerView的视图复用机制,所有item都会被一次性加载,性能拉胯。
更好的做法是把RecyclerView上方的布局(如果有的话)做成RecyclerView的Header,让整个滚动由RecyclerView自己处理:
1. 修改Adapter,添加Header类型
public class CustomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int VIEW_TYPE_HEADER = 0; private static final int VIEW_TYPE_ITEM = 1; // 数据集合和头部视图的相关逻辑 private List<String> mData; public CustomAdapter(List<String> data) { mData = data; } @Override public int getItemViewType(int position) { // 第一个位置是Header return position == 0 ? VIEW_TYPE_HEADER : VIEW_TYPE_ITEM; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); if (viewType == VIEW_TYPE_HEADER) { // 加载Header布局 View headerView = inflater.inflate(R.layout.your_header_layout, parent, false); return new HeaderViewHolder(headerView); } else { // 加载普通item布局 View itemView = inflater.inflate(R.layout.your_item_layout, parent, false); return new ItemViewHolder(itemView); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof ItemViewHolder) { // 绑定普通item的数据,注意position要减1(跳过Header) String itemData = mData.get(position - 1); ((ItemViewHolder) holder).tvContent.setText(itemData); } // Header不需要绑定数据的话直接跳过 } @Override public int getItemCount() { // 总数量=Header1个+数据数量 return mData.size() + 1; } // Header的ViewHolder static class HeaderViewHolder extends RecyclerView.ViewHolder { public HeaderViewHolder(View itemView) { super(itemView); // 初始化Header的控件 } } // 普通item的ViewHolder static class ItemViewHolder extends RecyclerView.ViewHolder { TextView tvContent; public ItemViewHolder(View itemView) { super(itemView); tvContent = itemView.findViewById(R.id.tv_content); } } }
2. 修改布局文件,直接用RecyclerView替代NestedScrollView
<android.support.v7.widget.RecyclerView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
这样改完之后,你原来写的SmoothScroller代码就能正常工作了,同时还能保留RecyclerView的视图复用,性能更优。
内容的提问来源于stack exchange,提问作者Darshana
相关产品推荐
相关产品推荐

