Custom List with lazy loading

狂风中的少年 提交于 2019-12-01 13:15:12
Nate

Assuming you start with the code I posted in this answer, and use the code you show in this question to download images from a list of URLs, then you should be able to achieve lazy image loading with the following changes:

Create a listener interface that gets notified when downloads complete:

public interface DownloadListener {
   // invokes if download success
   public void downloadSuccess(Bitmap bitmap);

   // invokes if download failed
   public void errorOccured();
}

Then, the Manager subclass that represents one list row, CustomListRow, is modified to implement this interface, and update the _thumb image when the download completes:

public class CustomListRow extends Manager implements DownloadListener, FieldChangeListener {

   public void downloadSuccess(final Bitmap img) {
      _data.setThumb(img);
      // make sure bitmap is updated on the UI / main thread
      UiApplication.getUiApplication().invokeLater(new Runnable() {
         public void run() {
            _thumb.setBitmap(img);
         }
      });
   }

   public void errorOccured() {
       // TODO: handle error
   }

Then, you'll need to add some code to create all your threads to download images in the background, and notify the DownloadListeners when the image downloads complete. You can decide where to do this. In my example, I will do this in my ListScreen class, where I instantiate the ListRander data objects and the CustomListField:

     for (int i = 0; i < numberOfItem; i++) {
        ListRander lr = new ListRander("Product Name " + i, icon);  // icon is placeholder for thumbnail image
        data.addElement(lr);
     }
     final CustomListField list = new CustomListField(data);
     add(list);
     list.setChangeListener(this);

     pool = new ThreadPool(3);  // 3 concurrent download threads

     for (int i = 0; i < numberOfItem; i++) {
        final int row = i;

        // create a new runnable to download the next image, and resize it:
        pool.assign(new Runnable() {
           public void run() {
                 try {            
                    String text=object[row].getJSONArray("UrlArray").getString(0).toString();
                    EncodedImage encodedImg = JPEGEncodedImage.encode(connectServerForImage(text), quality);    //connectserverForImage load Images from server                     
                    EncodedImage logoThumbnail = sizeImage(encodedImg, Display.getWidth(), Display.getHeight()-100);
                    list.getRow(row).downloadSuccess(logoThumbnail.getBitmap());
                 } catch (Exception e) {
                    e.printStackTrace();
                    list.getRow(row).errorOccured();
                 }
              }
           }
        });
     }

You could do this in the ListScreen constructor, or whenever you have your object[] array of URLs.

You'll need to add a new method to CustomListField:

public CustomListRow getRow(int row) {
    return (CustomListRow)getField(row);
}

The code above also needs a member variable added (in ListScreen) to create a thread pool:

private ThreadPool pool;

This thread pool implementation is based on this tutorial here, with very minor modifications, simply to change ArrayList (not available on BlackBerry Java) to Vector ... and removing the calls to Thread#destroy(). You'll need to copy that tutorial's ThreadPool, WorkerThread, and Done classes to your project. The above example I show creates a thread pool of 3 threads. On a single CPU smartphone, 2 or 3 threads is probably fine for your needs. Read more here if you want to get the perfect number of threads for your application.

Note: if possible, you can usually improve performance if you download images that are exactly the right size for your application, instead of burdening the network with larger images, and then resizing them inside your app. However, I realize that this depends on having some control over the images' web server, which you may not have. Just a thought for later optimization.

I am sure that I seen a good answer to this question on my travels, but can't find it now. I do recommend reviewing the BB forums here: http://supportforums.blackberry.com/t5/Java-Development/bd-p/java_dev as there are similar questions there.

For now, just the highlights of what you need to do:

  1. Create an image downloading runnable to process the download - you have pretty much already done this in your previous code.
  2. Use the Observer pattern (search the internet for this), so that the BitmapField is the Observer for the completion of your image downloading. So when the image has been downloaded, the Runnable invokes the observer, which can then update the Bitmap.
  3. Use a Thread pool with a limited number of Threads (I would say 3), so that you do not start a whole load of image downloads at the same time. Search the internet for information on Thread Pool for help implementing this. You had not done this step in your previous example, and you can get away with running all the downloads, but I expect at some stage that this will fail.

Put these together and you have your solution. Not trivial I know. Good luck.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!