5

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

我对 SimpleCursorAdapter 进行了子类化,并使用了 Filterable 和 SectionIndexer。

我想首先显示记录的名称,然后将计算放入一个线程中,然后在完成后显示。

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

4

2 回答 2

2

由于 android 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 的更多信息,请参阅此

于 2010-03-27T09:32:29.690 回答
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都会调用。如果尚未计算信息,您可以将自定义代码放入其中以执行诸如放置“正在加载...”之类的操作,或者如果已计算,则实际显示它。

顺便说一句,你在这个需要这么长时间的列表中实际计算的是什么?

于 2010-03-26T23:56:12.903 回答