在 RecyclerView 网格中放置一个不确定的进度条作为页脚

Posted

技术标签:

【中文标题】在 RecyclerView 网格中放置一个不确定的进度条作为页脚【英文标题】:Put an indeterminate progressbar as footer in a RecyclerView grid 【发布时间】:2015-01-18 14:24:58 【问题描述】:

如何在网格 RecycleView 中为“向上滚动以加载更多”获取不确定的圆形指示器?

此处描述了该模式:http://www.google.com/design/spec/components/progress-activity.html#progress-activity-behavior 在“两阶段加载”和“示例 2:向上滚动以加载更多”示例视频中。

我正在尝试使用新的 RecyclerView 来完成此操作,但我找不到“不太老套”的方法来做到这一点,首先是因为没有办法添加覆盖整行的页脚在网格中。有什么建议吗?

【问题讨论】:

我也在努力让它发挥作用。单列行很容易,但当您有多列时(如GridView),看起来就不好看了。另外,我们需要使用RecyclerView还是可以使用GridView来完成? @kgrevehagen 对于“我们需要使用 RecyclerView 还是可以使用 GridView 来完成?”我想我几乎可以肯定地回答这个问题:在this video,Chris Banes 在 7:36 说 RecyclerView“打算取代 ListView 和 GridView”,所以肯定是的,我们应该更新我们的应用程序以使用这个视图,因为“正式”它取代了其他旧观点。问题是实际上我们不能在不重写该视图的情况下为其添加页脚和/或页眉。我请 Chris 或任何其他 Google 雇主回答这个问题:/ *重写 = 扩展 github.com/pnikosis/materialish-progress这个库可以帮你 @AmrutBidri 可能你误解了这个问题:不是进度条(或微调器,在这种情况下),而是 RecyclerView 中的页脚。无论如何,感谢您的评论:) 【参考方案1】:

这很简单。

解决方案是使用与GridLayoutManager 相同的LinearLayoutManager 方法,然后在LayoutManager 上使用setSpanSizeLookup 方法,如下所示:

mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() 
        @Override
        public int getSpanSize(int position) 
            switch(myAdapter.getItemViewType(position))
                case MyAdapter.VIEW_TYPES.Product:
                    return 1;
                case MyAdapter.VIEW_TYPES.Progress:
                    return 2; //number of columns of the grid
                default:
                    return -1;
            
        
    );

这将自动使项目覆盖网格的整行(如果该行不是完全为空,则该项目转到下一行)。

【讨论】:

是的,这完全回答了我的问题。我唯一必须注意的是,这种制作页眉和页脚的方法仅对 GridLayoutManager 有效,对交错的无效。 在我的情况下一切正常,但是当我滚动回收器视图项目时,它会在底部显示进度条,但进度条无限显示直到我向上滚动是关闭进度条的方式加载完成后自动 @GanpatKaliya 你的进度条应该是一个临时项目,你应该在加载新项目时删除它 你的答案与@Vasily Kabunov 的结合,像魅力一样工作 太棒了!很有帮助【参考方案2】:

注意下面的解决方案有一些潜在的问题和限制,修改后的解决方案请检查这个Adding items to Endless Scroll RecyclerView with ProgressBar at bottom

这是我最近提出的解决方案: 这个想法是让 RecyclerView 有两种类型的项目,一种是我们常用的项目,第二种是进度条,然后我们需要监听滚动事件并决定我们是否要加载更多并显示进度条。 所以从idea到示例代码

progress_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_
              android:layout_>

    <ProgressBar
        android:layout_
        android:layout_
        android:id="@+id/progressBar"
        android:indeterminate="true"
        style="@android:style/Widget.Holo.ProgressBar"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                xmlns:ring="http://schemas.android.com/apk/res-auto"
                android:layout_
                android:layout_
                tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_
        android:layout_/>
</RelativeLayout>

EndlessRecyclerOnScrollListener.java

public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener 
    public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();

    private int previousTotal = 0; // The total number of items in the dataset after the last load
    private boolean loading = true; // True if we are still waiting for the last set of data to load.
    private int visibleThreshold = 1; // The minimum amount of items to have below your current scroll position before loading more.
    int firstVisibleItem, visibleItemCount, totalItemCount;

    private int current_page = 1;

    private LinearLayoutManager mLinearLayoutManager;

    public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) 
        this.mLinearLayoutManager = linearLayoutManager;
    

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) 
        super.onScrolled(recyclerView, dx, dy);

        visibleItemCount = recyclerView.getChildCount();
        totalItemCount = mLinearLayoutManager.getItemCount();
        firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();

        if (loading) 
            if (totalItemCount > previousTotal+1) 
                loading = false;
                previousTotal = totalItemCount;
            
        
        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) 
        // End has been reached
        // Do something
            current_page++;
            onLoadMore(current_page);
            loading = true;
        
    

    public abstract void onLoadMore(int current_page);

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> 
    private final int VIEW_ITEM = 1;
    private final int VIEW_PROG = 0;

    private List<String> mDataset;

    public static class TextViewHolder extends RecyclerView.ViewHolder 
        public TextView mTextView;
        public TextViewHolder(View v) 
            super(v);
            mTextView = (TextView)v.findViewById(android.R.id.text1);
        
    
    public static class ProgressViewHolder extends RecyclerView.ViewHolder 
        public ProgressBar progressBar;
        public ProgressViewHolder(View v) 
            super(v);
            progressBar = (ProgressBar)v.findViewById(R.id.progressBar);
        
    

    public MyAdapter(List<String> myDataset) 
        mDataset = myDataset;
    

    @Override
    public int getItemViewType(int position) 
        return mDataset.get(position)!=null? VIEW_ITEM: VIEW_PROG;
    

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        RecyclerView.ViewHolder vh;
        if(viewType==VIEW_ITEM) 
            View v = LayoutInflater.from(parent.getContext())
                    .inflate(android.R.layout.simple_list_item_1, parent, false);

            vh = new TextViewHolder(v);
        else 
            View v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.progress_item, parent, false);

            vh = new ProgressViewHolder(v);
        
        return vh;
    

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) 
        if(holder instanceof TextViewHolder)
            ((TextViewHolder)holder).mTextView.setText(mDataset.get(position));
        else
            ((ProgressViewHolder)holder).progressBar.setIndeterminate(true);
        
    

    @Override
    public int getItemCount() 
        return mDataset.size();
    

最后是 MainActivity.java

package virtoos.com.testapps;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;


public class MainActivity extends Activity 
    private RecyclerView mRecyclerView;
    private LinearLayoutManager mLayoutManager;
    private MyAdapter mAdapter;
    private final List<String> myDataset = new ArrayList<>();
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        handler = new Handler();
        addItems(20);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);

        //mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        mRecyclerView.setOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager) 
            @Override
            public void onLoadMore(int current_page) 
                //add progress item
                myDataset.add(null);
                mAdapter.notifyItemInserted(myDataset.size());
                handler.postDelayed(new Runnable() 
                    @Override
                    public void run() 
                        //remove progress item
                        myDataset.remove(myDataset.size() - 1);
                        mAdapter.notifyItemRemoved(myDataset.size());
                        //add items one by one
                        for (int i = 0; i < 15; i++) 
                            myDataset.add("Item"+(myDataset.size()+1));
                            mAdapter.notifyItemInserted(myDataset.size());
                        
                        //or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
                    
                , 2000);
                System.out.println("load");
            
        );

    

【讨论】:

这个答案(我也在另一个问题中看到)只能使用 LinearLayoutManager。我需要使用的是一个 GridLayoutManager(也是交错的),所以这个系统在这些情况下将无法工作:) 为什么不呢?您需要做的很小的修改是使进度条项目占据整行,因此您需要添加一些空项目,即假设您的网格中有 3 列,因此可能的空项目数量为:1、2、3然后是进度项,然后是另一个空项目,我希望如何找到空项目的数量是清楚的(data.size%3 +1) 嗨 Vilen,我对上面的代码有疑问 - 我在这里发布了一个新问题:***.com/questions/30681905/… @Simon 嗨。感谢您关注我的回答,一旦我靠近电脑,我会看看您的问题。 @VilenMelkumyan 页脚没有将 match_parent 作为其宽度?【参考方案3】:

这是对RecyclerView.Adapter 上的@Vilen Melkumyan 答案的一个小修改,它对我来说效果更好。您可以以任何方式使用您的EndlessRecyclerOnScrollListener 来加载数据,也可以随时启用或禁用页脚。

PS:它适用于GridLayoutManager

public class MyRecyclerViewAdapter  extends RecyclerView.Adapter<RecyclerView.ViewHolder> 

private final int VIEW_TYPE_ITEM = 1;
private final int VIEW_TYPE_PROGRESSBAR = 0;
private boolean isFooterEnabled = true;

private List<String> items;

public static class TextViewHolder extends RecyclerView.ViewHolder 
    public TextView mTextView;
    public TextViewHolder(View v) 
        super(v);
        mTextView = (TextView)v.findViewById(android.R.id.text1);
    

public static class ProgressViewHolder extends RecyclerView.ViewHolder 
    public ProgressBar progressBar;
    public ProgressViewHolder(View v) 
        super(v);
        progressBar = (ProgressBar)v.findViewById(R.id.progressBar);
    


public MyRecyclerViewAdapter(List<String> myDataset) 
    items = myDataset;


@Override
public int getItemCount() 
    return  (isFooterEnabled) ? items.size() + 1 : items.size();


@Override
public int getItemViewType(int position) 
    return (isFooterEnabled && position >= items.size() ) ? VIEW_TYPE_PROGRESSBAR : VIEW_TYPE_ITEM;


/**
 * Enable or disable footer (Default is true)
 *
 * @param isEnabled boolean to turn on or off footer.
 */
public void enableFooter(boolean isEnabled)
    this.isFooterEnabled = isEnabled;


@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    RecyclerView.ViewHolder vh;
    if(viewType== VIEW_TYPE_ITEM) 
        View v = LayoutInflater.from(parent.getContext())
                .inflate(android.R.layout.simple_list_item_1, parent, false);
        vh = new TextViewHolder(v);
    else 
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.progressbar, parent, false);

        vh = new ProgressViewHolder(v);
    
    return vh;


@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) 
    if(holder instanceof ProgressViewHolder)
        ((ProgressViewHolder)holder).progressBar.setIndeterminate(true);
     else if(items.size() > 0 && position < items.size()) 
        ((TextViewHolder)holder).mTextView.setText(items.get(position));            
    


我的 2 美分,平安!!

【讨论】:

页脚没有以 match_parent 作为其宽度?【参考方案4】:

在https://github.com/ramirodo/endless-recycler-view-adapter 中查看我的解决方案 或https://bintray.com/ramiro/android/endless-recycler-view-adapter。那里有一个示例以及在项目中设置库的步骤。

您只需要通过实现所需的方法来扩展您的回收器视图适配器。您还可以设置进度页脚的布局。

【讨论】:

【参考方案5】:

您可以通过将代码插入适配器来简化 Bronx 的回答。

public class ArticleGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> 
    private final int VIEW_ITEM = 0;
    private final int VIEW_LOADING = 1;

    private Context mContext;
    private List<Article> mArticles = new ArrayList<>();
    private RecyclerView mRecyclerView;
    private GridLayoutManager mManager;

    public ArticleGridAdapter(Context context, List<Article> articles, RecyclerView recyclerView) 
        this.mContext = context;
        this.mArticles = articles;
        this.mRecyclerView = recyclerView;
        this.mManager = (GridLayoutManager) recyclerView.getLayoutManager();

        mManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() 
            @Override
            public int getSpanSize(int position) 
                return getItemViewType(position) == VIEW_LOADING ? mManager.getSpanCount() : 1;
            
        );
    

【讨论】:

以上是关于在 RecyclerView 网格中放置一个不确定的进度条作为页脚的主要内容,如果未能解决你的问题,请参考以下文章

如何在 tkinter ~ python 中放置在网格中的两个小部件之间添加空间?

如何在 UIImage 中放置一个按钮来确定不同的选择?

如何自动确定 RecyclerView 网格的项目适合计数

如何在骨干模型中放置按钮?

在 Xamarin 轮播页面中放置一个计时器

在网站中放置广告的最佳做法是啥?