Android无尽列表

时间:2009-07-03 21:00:19

标签: android list listview

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

10 个答案:

答案 0 :(得分:268)

一种解决方案是实施OnScrollListener并在其onScroll方法中以方便的状态对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;
        }
    }
}

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

答案 1 :(得分:94)

只想提供我用于我的应用的解决方案。

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

  1. 让您的ListFragmentListActivity实施OnScrollListener
  2. 将以下方法添加到该类:

    @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,则用户必须滚动到列表的最后才能加载更多项目。

  3. (可选)如您所见,只有在用户停止滚动时才会调用“load-more check”。为了提高可用性,您可以通过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_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dp" >
    
        <ProgressBar
            android:id="@+id/progressBar1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_gravity="center_vertical" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/progressBar1"
            android:padding="5dp"
            android:text="@string/loading_text" />
    
    </RelativeLayout>
    
  4. (可选)最后,如果没有更多项目或网页,请致电listView.removeFooterView(yourFooterView)删除该加载指标。

答案 2 :(得分:20)

您可以在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

答案 3 :(得分:4)

Ognyan Bankov GitHub我发现了一个简单而有效的解决方案!

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

给定代码演示:

  1. 由HTTP分页请求填充的ListView。
  2. 使用NetworkImageView。
  3. “无尽的”ListView分页与预读。
  4. 为了将来的一致性forked Bankov's repo

答案 4 :(得分:2)

这是一个解决方案,可以在ListView加载时轻松显示加载视图。

您可以在此处查看课程:

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java - Helper使得可以使用这些功能而无需从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。您可以直接使用此类或从中扩展。

答案 5 :(得分:1)

可能有点迟,但以下解决方案对我来说非常有用。 在某种程度上,您需要做的就是为{List}添加Footer并为其创建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");
    }
});

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

答案 6 :(得分:0)

我一直在使用另一个非常类似的解决方案,但是,我正在使用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) {

    }
});

答案 7 :(得分:0)

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

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

以下是地址:https://github.com/SphiaTower/AutoPagerRecyclerManager

答案 8 :(得分:0)

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

以下是一个示例用法:EndlessScrollListActivity

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

答案 9 :(得分:0)

我知道这是一个古老的问题,Android世界基本上已经转移到RecyclerViews,但是对于任何有兴趣的人,您可能会发现this库非常有趣。

它使用与ListView一起使用的BaseAdapter来检测列表何时滚动到最后一个项目,或者何时滚动到远离最后一个项目的地方。

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

简单用法:

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中创建按钮并将其放置在上面的适配器代码中即可。

享受。