如何安排繁重的工作以便以后显示Listviews?

时间:2010-03-26 22:58:08

标签: android listview multithreading cursor

我有一个包含200个项目的列表视图。我为每一行使用自定义视图。有一段代码需要一些时间来计算,因此列表会在滚动时挂起并以慢速(2-3秒)加载。

我已经使用了SimpleCursorAdapter,并使用了Filterable和SectionIndexer。

我想最初显示记录的名称,并在计算中添加一个线程,并在完成后显示。

如何推迟一些工作,稍后更新列表视图以包含计算数据?这应该在没有用户交互的情况下即时显示。

2 个答案:

答案 0 :(得分:2)

由于Android UI是单线程的,因此只要执行长时间操作,它就会被阻止。

你可以做普通的java线程,并在主UI类中有一个Handler(),你可以将信息反馈到。但..

最简单的方法是创建一个AsyncTask()。它们是java线程,略有变化。他们可以在主计算之前/之后执行UI操作。例如:

 private class AndroidThread extends AsyncTask {
      protected Object doInBackground(Object... params) {
           return result;
      }

      protected void onPostExecute(Object result) {
           // do things here with the result of the doInBackground function
           // Like: "UiElement"."performTask(result)"
      }
 }

有关AsyncTask see this

的详细信息

答案 1 :(得分:1)

嗯,有趣的问题。以下是一些简单的想法:

1)将可以直接检索的值(即放置记录的名称)直接放入列表中。保留默认值,例如“正在加载...”,以便计算可能需要一段时间的所有内容。

2)创建第二个线程来进行计算,但要确保它不计算所有内容使用ListView的onScrollStateChanged监听器。这使您只能在列表空闲或通过触摸滚动缓慢滚动时执行后台工作。换句话说,当它处于'fling'模式时不要打扰,因为在计算完成之前行将会很久。

3)我不完全确定如何将线程中的加载阶段排入队列。你只想拥有一个加载线程 - 为每一行打开一个新线程会相当混乱而且毫无意义,因为手机不是多核 - 但这意味着线程必须有一个排队系统。我的意思是线程必须知道哪些行正在等待计算,但我还没有想到你如何做到这一点。但是,这也必须与哪些行可见相关联。对于列表中较长的行,线程计算信息没有任何意义。

4)只要加载线程完成特定行,就可以在列表适配器上调用notifyDatasetChanged()。如果ListView尝试刷新每一行的数据而不仅仅是那些可见的数据,这可能会有点沉重,但你必须试验看。

我希望这些想法有所帮助!


回应评论:假设您询问何时可见行,您可以通过实现ListView.OnScrollListener来实现。我从ApiDemos List13中获取灵感,但这里是相关的代码:

public class YourClass extends ListActivity implements ListView.OnScrollListener {
    public void onCreate(Bundle sIS){
        (...)
        // Note, you may have to write your own list adapter, extending BaseAdapter
        // See List13 in Api Demos for an example if you're not sure how to do that
        setListAdapter(yourImageAdapter); 
        getListView().setOnScrollListener(this);
    }

    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
            int totalItemCount) {
        Log.d(LOG_TAG, "onScroll, firstVisibleItem = " + firstVisibleItem +
                ", visibleItemCount = " + visibleItemCount
                + ", totalItemCount = " + totalItemCount);
    }

    public void onScrollStateChanged(AbsListView view, int scrollState) {
        Log.d(LOG_TAG, "onScrollStateChanged called, scrollState = " + scrollState);
        switch (scrollState) {
        case OnScrollListener.SCROLL_STATE_IDLE:
            int first = view.getFirstVisiblePosition();
            int count = view.getChildCount();

            Log.d(LOG_TAG, "first = " + first + ", count = " + count);

            break;
        case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
            //Scrolling with the finger touching the screen
            break;

        case OnScrollListener.SCROLL_STATE_FLING:
            // User 'threw' the list up or down and it's scrolling rapidly
            break;
        }
    }
}

它告诉你关于你的列表的一切:它是否滚动,可见的是什么等。如果你使用加载线程只对可见行进行后台工作,这应该有助于减少不必要的开销。

关于BaseAdapter的另一点:我没有尝试使用任何其他形式的列表适配器,所以也许它们与所有这些相同,但BaseAdapter的一个非常有用的事情是它可以让你处理每行的'膨胀'个别。每次列表即将显示新行时,都会调用getView。您可以在其中放置自定义代码,以便在尚未计算信息的情况下执行“加载...”等操作,或者实际显示该信息(如果有)。

顺便说一下,你在这个列表中实际计算了多长时间?