在ScrollView中平滑滚动RecyclerViews

时间:2017-11-30 12:11:02

标签: android android-recyclerview recycler-adapter

我希望将回收站视图滚动为一张,但无法达到所需的行为。 我的布局结构是:

<ScrollView
android:id="@+id/search_results_scroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:isScrollContainer="false"
android:nestedScrollingEnabled="true"
android:scrollbars="none">


<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        style="@style/search_results_group_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="RecyclerView 1 Title" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/search_results_recycler"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        style="@style/search_results_group_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="RecyclerView 1 Title" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/some_static_results_recycler"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:nestedScrollingEnabled="false" />

</LinearLayout>

我的第一个回收商获取新的数据部分,当它滚动到最后时,我想显示其下的其余项目。

首先尝试用 NestedScrollView 制作它,但它的性能是不可接受的,所以我想坚持 ScrollView 。参数的不同变化,自定义布局管理器,无助于达到所需的行为。 据我了解,我需要坚持ScrollView scrollListener,并禁用回收器内部滚动,但在这种情况下,我只获得部分填充 recyclerView (6项而不是20项),即使 recyclerView android:layout_height="wrap_content"。我使用 LinearLayoutManagers setAutoMeasureEnabled=true. 所以我的问题是: - 如何在添加新项目后(超过屏幕高度)扩展recyclerView高度? - 在这种情况下如何更好地处理滚动?

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:0)

感谢您建议的解决方案链接!

不幸的是,这些方法无法解决回收器中滚动卡顿的问题。

当新的数据部分插入第一个回收站时,一切都工作得非常慢,导致scrollView重新计算每个子项的高度,其内容正在发生变化(同样的问题出现在NestedScrollView中)。

通过在ScrollView或NestedScrollView中内联它来杀死所有RecyclerView效率。

我用另一种方式解决了这个问题:

我使用一个RecyclerView 并在其中包含部分。使用自定义ViewHolder 自定义适配器行为实现此目的。

通过这种方法,我的回收工作非常顺畅,快速。

所以我在RecyclerView中的项目结构:

  • 第一个TextView标题
  • 第一个动态填充的项目部分
  • 第二个TextView标题
  • 第二项
  • TextView页脚

对于我声明自定义ViewHolder的标题:

protected class HeaderViewHolder extends BaseListAdapter.ViewHolder implements View.OnClickListener {
    View view;
    TextView textView;

    protected HeaderViewHolder(View v) {
        super(v);
        this.view = v;
        textView = this.view.findViewById(R.id.header_text);
        v.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (itemClickListener != null)
            itemClickListener.onItemClick(v, this.getLayoutPosition());
    }

    public View getView() {
        return this.view;
    }

    // Set custom header text
    public void setTitle(String title) {
        textView.setText(title);
    }

    // Hide or show title, if items section don't have any items
    public void setVisibility(int visibility) {
        textView.setVisibility(visibility);
    }
}

标题ViewHolder的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/search_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

CustomListAdapter用于填充和管理回收站部分:

public class CustomListAdapter extends BaseListAdapter<RecyclerItem> {

    private static final int FOOTER_TYPE = 10;
    private static final int FIRST_HEADER_TYPE = 25;
    private static final int SECOND_HEADER_TYPE = 50;
    private static final int SECOND_ITEMS_TYPE = 100;

    private List<RecyclerItem> secondRecyclerItems = new ArrayList<>();

    public CustomListAdapter(List<RecyclerItem> items, Context context) {
        super(items, R.layout.first_recycler_item, context);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case FIRST_HEADER_TYPE:
            case SECOND_HEADER_TYPE:
                HeaderViewHolder holder = new HeaderViewHolder(View.inflate(parent.getContext(), R.layout.header_layout, null));
                holder.setTitle(parent.getResources().getString(viewType == FIRST_HEADER_TYPE ? R.string.first_header_title : R.string.second_header_title));
                return holder;

            case FOOTER_TYPE:
                return new HeaderViewHolder(View.inflate(parent.getContext(), R.layout.footer_layout, null));
            default:
                return super.onCreateViewHolder(parent, viewType);
        }
    }

    @Override
    public void onBindViewHolder(BaseListAdapter.ViewHolder holder, int position) {

        if (holder instanceof HeaderViewHolder) {
            if (position == 0) {
                ((HeaderViewHolder) holder).setVisibility(items.size() == 0 ? View.GONE : View.VISIBLE);
            } else if (position == super.getItemCount() + 1 || position == getItemCount() - 1) {
                ((HeaderViewHolder) holder).setVisibility(secondRecyclerItems.size() == 0 ? View.GONE : View.VISIBLE);
            }
            return;
        }

        RecyclerItem item = position <= items.size()
                ? items.get(position - 1)
                : secondRecyclerItems.get(position - (items.size() + 2));


        // Here you populate your items with data, load images, etc...
        // TextView title = holder.itemView.findViewById(R.id.first_recycler_item_title);        
    }

    public void clearFirstRecyclerItems() {
        items.clear();
        notifyDataSetChanged();
    }

    public void clearSecondRecyclerItems() {
        secondRecyclerItems.clear();
        notifyDataSetChanged();
    }

    // Define separate collection for second recycler items. First recycler will be populated by default base adapter
    public void swapSecondRecyclerItems(List<RecyclerItem> list) {
        if (secondRecyclerItems != null) {
            secondRecyclerItems.clear();
            secondRecyclerItems.addAll(list);
        } else {
            secondRecyclerItems = new ArrayList<>();
            secondRecyclerItems.addAll(list);
        }
        notifyDataSetChanged();
    }

    // Handle items click, and skip clicks on headers
    public RecyclerItem getClickedItem(int position) {
        if (position == super.getItemCount() + 1 || position == 0 || position == getItemCount() - 1)
            return null;

        return position <= items.size()
                ? items.get(position - 1)
                : secondRecyclerItems.get(position - (items.size() + 2));
    }

    // That's the core method to split recycler items into sections, depending on their position
    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return FIRST_HEADER_TYPE;
        } else if (position == super.getItemCount() + 1) {
            return SECOND_HEADER_TYPE;
        } else if (position == getItemCount() - 1) {
            return FOOTER_TYPE;
        } else if (position > super.getItemCount() + 1) {
            return SECOND_ITEMS_TYPE;
        }
        return super.getItemViewType(position);
    }

    // Redefine overall items quantity - super.getItemCount() (base recyclerAdapter items) + secondRecyclerItems + 3 (2 headers and 1 footer)
    @Override
    public int getItemCount() {
        return super.getItemCount() + secondRecyclerItems.size() + 3;
    }

    public int getFirstRecyclerItemsCount() {
        return super.getItemCount() + 1;
    }
}

完整性BaseListAdapter:

public abstract class BaseListAdapter<T> extends RecyclerView.Adapter<BaseListAdapter.ViewHolder> {
    protected List<T> items;
    protected @android.support.annotation.LayoutRes int layout;
    protected Context context;
    protected OnItemClickListener itemClickListener;

    public BaseListAdapter(List<T> items, @android.support.annotation.LayoutRes int layout, Context context) {
        this.items = items;
        this.layout = layout;
        this.context = context;
    }

    public T getItem(int position) {
        return items.get(position);
    }

    public List<T> getItems() {
        return items;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.itemClickListener = onItemClickListener;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public BaseListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);

        return new ViewHolder(v);
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return items.size();
    }

    /**
     * Update the recycler view with a new dataset.
     * */
    public void swap(List<T> list){
        if(items != null) {
            items.clear();
            items.addAll(list);
        }
        else {
            items = new ArrayList<>();
            items.addAll(list);
        }
        notifyDataSetChanged();
    }

    public void add(List<T> list){
        items.addAll(list);
        notifyDataSetChanged();
    }

    protected class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        private View view;

        protected ViewHolder(View v) {
            super(v);
            this.view = v;
            v.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            if(itemClickListener != null)
                itemClickListener.onItemClick(v, this.getLayoutPosition());
        }

        public View getView() {
            return this.view;
        }
    }

    public interface OnItemClickListener
    {
        void onItemClick(View v, int position);
    }
}

希望它可以帮助别人。