Android无尽列表


Answers:


269

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

下面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应用程序)。


3
呵呵,如果您停止在屏幕中间滚动,会不会触发适配器大小的增加?实际上只有到达列表底部时,才应该加载下一个10。
马提亚斯(Matthias)2010年

9
我注意到许多使用BaseAdapter的例子。想提一下,如果您使用的是数据库,请尝试使用SimpleCursorAdapter。当您需要添加到列表中时,更新数据库,获取新的游标,然后将SimpleCursorAdapter#changeCursor与notifyDataSetChanged一起使用。不需要子类,并且它的性能比BaseAdapter好(同样,仅当您使用数据库时)。
布拉克

1
调用notifyDataSetChanged()之后,它将成为列表的顶部,我该如何防止它?
绘制

2
注释-我使用了这种方法,但是需要添加对何时visibleCount为0的检查。对于每个列表,我都向onScroll获得了一个回调,其中firstVisible,visibleCount和totalCount都为0,从技术上讲满足条件,但不是当我想加载更多。
埃里克·米尔

5
此代码的另一个问题是,firstVisible + visibleCount >= totalCount通过多次调用侦听器可以多次满足该条件。如果加载更多功能是Web请求(很可能是),请添加另一个检查,以检查请求是否正在进行。另一方面,请检查totalCount是否不等于上一个总数,因为我们不需要列表中相同数量的项目的多个请求。
Subin Sebastian 2012年

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是应该触发加载过程的列表项的数量(从末开始计数)。如果设置threshold0,例如,用户滚动到列表的最末端,以装载更多的物品。

  3. (可选)如您所见,仅当用户停止滚动时才调用“加载更多检查”。为了提高可用性,您可以通过加载并在列表末尾添加一个加载指示器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)如果没有更多的项目或页面,则通过调用删除该加载指示器。


我试过了,但不适用于ListFragment。如何在ListFragment中使用它。
zeeshan

好吧,我ListFragment也可以毫无问题地使用它-只需按照上述每个步骤进行操作,就可以了;)还是可以提供任何错误消息?
saschoar

:你可能错过了在本SO答案解释stackoverflow.com/a/6357957/1478093 -getListView().setOnScrollListener(this)
TBieniek

7
注意:在listview上添加和删除页脚是有问题的(如果在添加页脚之前调用setAdapter则不会显示页脚),我最终直接修改了页脚视图的可见性而没有删除它。
DVD

必须在loadElements(currentPage)中添加什么????我的意思是我叫AsyncTask还是线程?
android_developer

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上运行)


7
除了返回in中的视图外,做其他事情是不好的做法getView。例如,您不能保证pos被用户查看。它可能只是ListView测量的东西。
托马斯·阿勒

我想在滚动10个项目后加载更多项目。但是我注意到加载是在第10个项目可见之前开始的,意味着第9个项目开始加载。那么如何停止呢?
Atul Bhardwaj 2012年

4

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

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

给定的代码演示:

  1. 由HTTP分页的请求填充的ListView。
  2. NetworkImageView的用法。
  3. “无尽”的ListView分页具有预读功能。

为了将来的一致性,我分叉了班科夫的回购协议


2

1

可能有些晚,但是以下解决方案对我而言非常有用。在某种程度上,您需要做的就是添加到ListView中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");
    }
});

当页脚可见并像吊饰一样工作时,将触发此事件一次。


0

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

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

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


链接断开。
DSlomer64'9


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) {

    }
});

0

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

它使用与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元素,可以单击该按钮或其他ui元素以从URL中获取更多数据。按照代码中的说明放置适配器时,适配器确切知道何时显示该按钮以及何时禁用该按钮。只需在xml中创建按钮并将其放置在上面的适配器代码中即可。

请享用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.