Android 无尽列表

Posted

技术标签:

【中文标题】Android 无尽列表【英文标题】:Android Endless List 【发布时间】:2010-11-08 00:35:44 【问题描述】:

如何创建一个列表,当您到达列表末尾时,我会收到通知,以便我可以加载更多项目?

【问题讨论】:

我推荐 CommonWare 的 EndlessAdapter:github.com/commonsguy/cwac-endless。我从另一个问题的答案中找到了它:***.com/questions/4667064/…。 我需要同样的东西..谁能帮忙?..***.com/questions/28122304/… 【参考方案1】:

一种解决方案是实现OnScrollListener 并在ListAdapteronScroll 方法中以方便的状态对ListAdapter 进行更改(如添加项目等)。

下面的ListActivity 显示了一个整数列表,从 40 开始,当用户滚动到列表末尾时添加项目。

public class Test extends ListActivity implements OnScrollListener 

    Aleph0 adapter = new Aleph0();

    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setListAdapter(adapter); 
        getListView().setOnScrollListener(this);
    

    public void onScroll(AbsListView view,
        int firstVisible, int visibleCount, int totalCount) 

        boolean loadMore = /* maybe add a padding */
            firstVisible + visibleCount >= totalCount;

        if(loadMore) 
            adapter.count += visibleCount; // or any other amount
            adapter.notifyDataSetChanged();
        
    

    public void onScrollStateChanged(AbsListView v, int s)      

    class Aleph0 extends BaseAdapter 
        int count = 40; /* starting amount */

        public int getCount()  return count; 
        public Object getItem(int pos)  return pos; 
        public long getItemId(int pos)  return pos; 

        public View getView(int pos, View v, ViewGroup p) 
                TextView view = new TextView(Test.this);
                view.setText("entry " + pos);
                return view;
        
    

您显然应该为长时间运行的操作(例如加载网络数据)使用单独的线程,并且可能希望在最后一个列表项中指示进度(例如市场或 gmail 应用程序)。

【讨论】:

嗯,如果您停止在屏幕中间滚动,这不会触发适配器大小的增加吗?它应该只在实际到达列表底部时加载接下来的 10 个。 我注意到很多使用 BaseAdapter 的例子。想提一下,如果您使用的是数据库,请尝试使用 SimpleCursorAdapter。当您需要添加到列表中时,更新您的数据库,获取一个新游标,并使用 SimpleCursorAdapter#changeCursor 和 notifyDataSetChanged。不需要子类化,它的性能比 BaseAdapter 更好(同样,仅当您使用数据库时)。 调用notifyDataSetChanged()后会排在首位,如何防止? 注意 - 我使用了这种方法,但需要添加检查 visibleCount 何时为 0。对于每个列表,我得到一个 onScroll 回调,其中 firstVisible、visibleCount 和 totalCount 都为 0,这技术上满足条件,但不是我想加载更多。 此代码的另一个问题是条件firstVisible + visibleCount >= totalCount 多次调用侦听器多次满足。如果加载更多功能是一个网络请求,(很可能是),添加另一个检查请求是否正在进行。另一方面,检查totalCount 是否不等于Previous 总计数,因为我们不需要对列表中相同数量的项目进行多次请求。【参考方案2】:

只是想贡献一个我用于我的应用程序的解决方案。

它也是基于OnScrollListener接口,但我发现它在低端设备上的滚动性能要好得多,因为在滚动操作期间不执行任何可见/总计数计算。

    让您的ListFragmentListActivity 实施OnScrollListener

    将以下方法添加到该类:

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) 
        //leave this empty
    
    
    @Override
    public void onScrollStateChanged(AbsListView listView, int scrollState) 
        if (scrollState == SCROLL_STATE_IDLE) 
            if (listView.getLastVisiblePosition() >= listView.getCount() - 1 - threshold) 
                currentPage++;
                //load more list items:
                loadElements(currentPage);
            
        
    
    

    其中currentPage 是应添加到列表中的数据源页面,threshold 是列表项的数量(从末尾开始计数),如果可见,则应触发加载过程。例如,如果您将threshold 设置为0,用户必须滚动到列表的最后才能加载更多项目。

    (可选) 如您所见,“加载更多检查”仅在用户停止滚动时调用。为了提高可用性,您可以通过listView.addFooterView(yourFooterView) 在列表末尾添加加载指示器。这种页脚视图的一个示例:

    <?xml version="1.0" encoding="utf-8"?>
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/footer_layout"
        android:layout_
        android:layout_
        android:padding="10dp" >
    
        <ProgressBar
            android:id="@+id/progressBar1"
            android:layout_
            android:layout_
            android:layout_centerVertical="true"
            android:layout_gravity="center_vertical" />
    
        <TextView
            android:layout_
            android:layout_
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/progressBar1"
            android:padding="5dp"
            android:text="@string/loading_text" />
    
    </RelativeLayout>
    

    (可选)最后,如果没有更多项目或页面,请调用 listView.removeFooterView(yourFooterView) 删除加载指示器。

【讨论】:

我试过这个,但它不适用于 ListFragment。在 ListFragment 中如何使用它。 好吧,我也将它与ListFragment 一起使用,没有任何问题 - 只需按照上述每个步骤操作即可;) 或者您能提供任何错误消息吗? 在这个 SO 答案中解释了您可能错过的内容:***.com/a/6357957/1478093 - getListView().setOnScrollListener(this) 注意:在列表视图上添加和删除页脚是有问题的(如果在添加页脚之前调用 setAdapter,页脚不会显示),我最终直接修改了页脚视图的可见性而不删除它。跨度> loadElements(currentPage)中需要添加什么???我的意思是我应该调用我的 AsyncTask 还是线程?【参考方案3】:

您可以在onScrollListener 的帮助下检测到列表的结尾,工作代码如下:

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) 
    if (view.getAdapter() != null && ((firstVisibleItem + visibleItemCount) >= totalItemCount) && totalItemCount != mPrevTotalItemCount) 
        Log.v(TAG, "onListEnd, extending list");
        mPrevTotalItemCount = totalItemCount;
        mAdapter.addMoreData();
    

另一种方法(在适配器内部)如下:

    public View getView(int pos, View v, ViewGroup p) 
            if(pos==getCount()-1)
                addMoreData(); //should be asynctask or thread
            
            return view;
    

注意这个方法会被多次调用,所以需要再添加一个条件来阻止addMoreData()的多次调用。

当您将所有元素添加到列表中时,请在您的适配器内调用notifyDataSetChanged() 以更新视图(它应该在 UI 线程上运行 - runOnUiThread)

【讨论】:

除了在getView 中返回视图之外,做其他事情是不好的做法。例如,您不能保证pos 会被用户查看。它可能只是 ListView 测量的东西。 我想在滚动 10 个项目后加载更多项目。但我注意到在第 10 项可见之前开始加载意味着第 9 项可见。那么如何停止呢?【参考方案4】:

Ognyan Bankov GitHub,我找到了一个简单有效的解决方案!

它利用 Volley HTTP library 使 Android 应用程序的网络更容易,最重要的是更快。 Volley 可通过开放的 AOSP 存储库获得。

给定的代码演示:

    由 HTTP 分页请求填充的 ListView。 NetworkImageView 的使用。 “无尽”ListView 分页与预读。

为了未来的一致性我forked Bankov's repo。

【讨论】:

【参考方案5】:

这是一个解决方案,它还可以在加载时轻松地在 ListView 的末尾显示加载视图。

您可以在此处查看课程:

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java - 无需从 SimpleListViewWithLoadingIndicator 扩展即可使用功能的帮助器。

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java - 当用户即将到达 ListView 底部时开始加载数据的监听器。

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java - EndlessListView。你可以直接使用这个类,也可以从它扩展。

【讨论】:

【参考方案6】:

可能有点晚了,但以下解决方案对我来说非常有用。 在某种程度上,您需要做的就是将 Footer 添加到您的 ListView 并为其创建 addOnLayoutChangeListener

http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)

例如:

ListView listView1 = (ListView) v.findViewById(R.id.dialogsList); // Your listView
View loadMoreView = getActivity().getLayoutInflater().inflate(R.layout.list_load_more, null); // Getting your layout of FooterView, which will always be at the bottom of your listview. E.g. you may place on it the ProgressBar or leave it empty-layout.
listView1.addFooterView(loadMoreView); // Adding your View to your listview 

...

loadMoreView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() 
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) 
         Log.d("Hey!", "Your list has reached bottom");
    
);

当页脚变得可见并像魅力一样工作时,此事件会触发一次。

【讨论】:

【参考方案7】:

这个问题的关键是检测load-more事件,启动异步数据请求,然后更新列表。还需要一个带有加载指示器和其他装饰器的适配器。事实上,在某些极端情况下,问题是非常复杂的。仅仅一个OnScrollListener 实现是不够的,因为有时项目不会填满屏幕。

我已经为RecyclerView 编写了一个支持无限列表的个人包,并且还提供了一个异步加载器实现AutoPagerFragment,这使得从多页源获取数据变得非常容易。它可以将您想要的任何页面加载到自定义事件的RecyclerView 中,而不仅仅是下一页。

这里是地址:https://github.com/SphiaTower/AutoPagerRecyclerManager

【讨论】:

链接已损坏。【参考方案8】:

到目前为止,我看到的最佳解决方案是在 FastAdapter 库中用于回收站视图。它有一个EndlessRecyclerOnScrollListener

这是一个示例用法:EndlessScrollListActivity

一旦我将它用于无限滚动列表,我就意识到该设置非常强大。我肯定会推荐它。

【讨论】:

【参考方案9】:

我一直在研究另一种与此非常相似的解决方案,但是,我使用footerView 为用户提供点击footerView 下载更多元素的可能性,我使用的是“菜单”,即显示在ListView上方和父视图的底部,这个“菜单”隐藏了ListView的底部,所以,当listView滚动时菜单消失,当滚动状态空闲时,菜单出现再次,但是当用户滚动到listView 的末尾时,我“询问”是否在这种情况下显示footerView,菜单不会出现,用户可以看到footerView 加载更多内容。代码如下:

问候。

listView.setOnScrollListener(new OnScrollListener() 

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) 
        // TODO Auto-generated method stub
        if(scrollState == SCROLL_STATE_IDLE) 
            if(footerView.isShown()) 
                bottomView.setVisibility(LinearLayout.INVISIBLE);
             else 
                bottomView.setVisibility(LinearLayout.VISIBLE);
             else 
                bottomView.setVisibility(LinearLayout.INVISIBLE);
            
        
    

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) 

    
);

【讨论】:

【参考方案10】:

我知道这是一个老问题,Android 世界已经大多转向 RecyclerViews,但对于任何感兴趣的人,您可能会发现 this 库非常有趣。

它使用与 ListView 一起使用的 BaseAdapter 来检测列表何时滚动到最后一项或何时滚动离开最后一项。

它带有一个示例项目(仅 100 行 Activity 代码),可用于快速了解其工作原理。

简单用法:

class Boy

private String name;
private double height;
private int age;
//Other code


存放 Boy 对象的适配器如下所示:


public class BoysAdapter extends EndlessAdapter<Boy>




        ViewHolder holder = null;


        if (convertView == null) 
            LayoutInflater inflater = LayoutInflater.from(parent
                    .getContext());

            holder = new ViewHolder();

            convertView = inflater.inflate(
                    R.layout.list_cell, parent, false);


            holder.nameView = convertView.findViewById(R.id.cell);

            // minimize the default image.
            convertView.setTag(holder);
         else 
            holder = (ViewHolder) convertView.getTag();
        

        Boy boy = getItem(position);

        try 
            holder.nameView.setText(boy.getName());

            ///Other data rendering codes.

         catch (Exception e) 
            e.printStackTrace();
        

        return super.getView(position,convertView,parent);


注意 BoysAdapter 的 getView 方法如何返回对 EndlessAdapter 超类的 getView 方法的调用。这是 100% 必不可少的。

现在要创建适配器,请执行以下操作:

   adapter = new ModelAdapter() 
            @Override
            public void onScrollToBottom(int bottomIndex, boolean moreItemsCouldBeAvailable) 

                if (moreItemsCouldBeAvailable)  
                    makeYourServerCallForMoreItems();
                 else 
                    if (loadMore.getVisibility() != View.VISIBLE) 
                        loadMore.setVisibility(View.VISIBLE);
                    
                
            

            @Override
            public void onScrollAwayFromBottom(int currentIndex)  
                loadMore.setVisibility(View.GONE);
            

            @Override
            public void onFinishedLoading(boolean moreItemsReceived)  
                if (!moreItemsReceived) 
                    loadMore.setVisibility(View.VISIBLE);
                
            
        ;

loadMore 项目是一个按钮或其他 ui 元素,可以单击它以从 url 获取更多数据。 当按照代码中的描述放置时,适配器确切地知道何时显示该按钮以及何时禁用它。只需在您的 xml 中创建按钮并将其放置在上面的适配器代码中即可。

享受吧。

【讨论】:

以上是关于Android 无尽列表的主要内容,如果未能解决你的问题,请参考以下文章

android中带有Endless Listview Scroll的AsynTask

是否可以在颤动中流式传输无尽的音频?

如何从 Parse.com 获取更多对象以获得无尽的 ListView

java 无尽的列表视图

如何创建一个循环(无尽的)RecyclerView?

Android进程绝杀技--forceStop(转)