如何在不冻结程序的情况下更新ListView

3
所以我有一个字符串(ISBN)列表,需要使用这些字符串获取相关的图书对象,并将其填充到listview中。问题是使用字符串获取图书对象的函数需要时间,因此获取30本书需要等待4或5秒钟。
我考虑了一种方法,即逐个获取图书对象,并在获取后将它们添加到列表中。但是此过程会冻结UI,直到完成添加所有图书为止。如果尝试将此过程放入新线程,它将不允许添加到任何UI对象(因为它来自另一个线程)。如果尝试将其放入AsyncTask中,则无法访问ListView,因为它位于MainActivity类中。
肯定有一种动态更新UI元素的方法,我肯定曾经见过它。有什么建议吗?
编辑:
以下是我实际用于向列表中添加项目的代码:
//List view and adapter setup
listView = (ListView) findViewById(R.id.listViewCheckout);
bookAdapter = new SearchBookAdapter(getApplicationContext(), R.layout.search_row_layout);
listView.setAdapter(bookAdapter);

for(int i = 0; i < searches.size(); i++) {

    //Get the book
    Book book = BackendFunctions.getBookFromISBN(fbSnapshot, searches.get(i));

    //Assign data to the adapter variables
    Bitmap cover = book.getCover();
    String title = book.getTitle();
    String author = book.getAuthor();

    //Add data to the adapter and set the list
    SearchBookDataProvider dataProvider = new SearchBookDataProvider(cover, title, author);
    bookAdapter.add(dataProvider);
    bookAdapter.notifyDataSetChanged();
}

1
尝试使用接口将书籍列表从异步任务传递到主活动。 - Aswin P Ashok
1
发布你的代码? - Sush
2
https://dev59.com/emcs5IYBdhLWcg3wym_D - OneCricketeer
@Ezio 在这里,回调函数怎么帮助我呢? - Vedvart1
1
将Listview实例传递给您的AsyncTask。 - nhoxbypass
显示剩余4条评论
5个回答

1

你能像这样更改你的代码吗?这样很简单,我认为它会起作用。

//List view and adapter setup
listView = (ListView) findViewById(R.id.listViewCheckout);
bookAdapter = new SearchBookAdapter(getApplicationContext(), R.layout.search_row_layout);
SearchBookDataProvider dataProvider;
listView.setAdapter(bookAdapter);

 new AsyncTask() {
            @Override
            protected Object doInBackground(Object[] objects) {
                for(int i = 0; i < searches.size(); i++) {

                //Get the book
                Book book = BackendFunctions.getBookFromISBN(fbSnapshot, searches.get(i));

                //Assign data to the adapter variables
                Bitmap cover = book.getCover();
                String title = book.getTitle();
                String author = book.getAuthor();

                //Add data to the adapter and set the list
                dataProvider = new SearchBookDataProvider(cover, title, author);
                bookAdapter.add(dataProvider);
                }
            }
            @Override
            protected void onPostExecute(Object o) {
                if (bookAdapter!= null) {
                    bookAdapter.notifyDataSetChanged();
                }
                super.onPostExecute(o);
            }
 }.execute();

当我运行这段代码时,出现了一个问题,错误提示为“只有创建视图层次结构的原始线程才能触摸其视图”,出现在bookAdapter.add(dataProvider)这一行。如果我将这一行移动到onPostExecute()中,程序会再次像最初一样冻结。 - Vedvart1
@Vedvart1,你对上面的代码做了任何修改吗? - Vivek Barai

0

你可以使用TimerTask来更新listview或者runUiThread()或doInBackground()。但是记得在更新列表时应该使用notifysetChanges()


您能否给一个例子?我不太确定您的意思。 - Vedvart1

0

我认为如果你愿意使用一个开源项目的话,我的建议是使用RX-JAVA。它基于响应式和推送模型。

rx-java链接

rx-java示例


0

步骤1:声明一个执行器服务

 private ExecutorService mExecuterService = null;

步骤2:声明一个类来进行列表迭代和视图更新

  class ListViewUpdater implements Runnable{

            public ListViewUpdater(/* if you need you can pass list params here */){

            }

            @Override
            public void run() {
                for(int i = 0; i < searches.size(); i++) {

                    //Get the book
                    Book book = BackendFunctions.getBookFromISBN(fbSnapshot, searches.get(i));

                    //Assign data to the adapter variables
                    Bitmap cover = book.getCover();
                    String title = book.getTitle();
                    String author = book.getAuthor();



                }
//below code is important for Updating UI ,you should run UI Updates in UI thread
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        bookAdapter.notifyDataSetChanged();
                    }
                });
            }
        }

步骤3:初始化并调用以下方法

//Add data to the adapter and set the list
    SearchBookDataProvider dataProvider = new SearchBookDataProvider(cover, title, author);
   bookAdapter.add(dataProvider);
    mExecuterService = Executors.newSingleThreadExecutor()
    mExecuterService.execute(new ListViewUpdater());

这可能解决你的问题。


我遇到的问题是除了创建ListView的那个类之外,没有其他类可以触及它,因此这个ListViewUpdated类无法修改它。 - Vedvart1
在另一个线程中使用UI对象,您需要使用runOnUiThread(new Runnable() { @Override public void run() { bookAdapter.notifyDataSetChanged(); } }); - CLIFFORD P Y
@CLIFFORD P Y runOnUiThread 部分的代码是否保证只会在 run() 中耗时的后台代码完成后才运行? - AJW
@AJW run() 方法中的代码是串行执行的。 - CLIFFORD P Y

-1

你可以在一个线程中获取书籍列表并发送带有接收到的数据的广播。 在你的MainActivity类中注册一个广播接收器,并在接收器中更新适配器。这样不会冻结UI。

编辑 -

  BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        Book book = (Book)intent.getSerializableExtra("Book");
        SearchBookDataProvider dataProvider = new SearchBookDataProvider(cover, title, author);
        bookAdapter.add(dataProvider);
        bookAdapter.notifyDataSetChanged();
    }
};

Thread thread = new Thread(){
    @Override
    public void run()
    {
        for(int i = 0; i < searches.size(); i++) {

            //Get the book
            Book book = BackendFunctions.getBookFromISBN(fbSnapshot, searches.get(i));

            Intent intent = new Intent();
            intent.setAction("Book Received");
            intent.putExtra("Book",book);
            sendBroadcast(intent);
        }
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    listView = (ListView) findViewById(R.id.listView);
    listView.setAdapter(bookAdapter);
    registerReceiver(broadcastReceiver,new IntentFilter("Book Received"));
    thread.start();

}

我知道这可能不是最有效的方法。但这是一种方法。 - Kshitij

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接