-1

I am attempting to load picture files in the form of thumbnails from my internal storage to a list view. Currently, I am using a ViewHolder, but the loading is still choppy when scrolling so I am going to try to use an AsyncTask. However I can't get my head around how to structure the AsyncTask as most of the examples I've found deal with downloading from a website. I'm not even sure if I should subclass it in my BaseAdapter or in my MainActivity. I have added my baseadapter below with the unfinished AsyncTask at the bottom. How do I structure this to either: use the AsyncTask to assist the ViewHolder, or directly pass an image to AsyncTask and have it return the bitmap so the ListView will scroll smoothly?

public class ListViewAdapter extends BaseAdapter {

private static final int WIDTH = 250;
private static final int HEIGHT = 250;
private static final int ROTATION = 90;

private final static String TAG = "Pictures";

private final ArrayList<SelfieObject> mItems = new ArrayList<SelfieObject>();
private Context mContext;
private File mStorageDir;
private String mFilePrefix;

public ListViewAdapter(Context context, File storageDir, String filePrefix) {
    mContext = context;
    mStorageDir = storageDir;
    mFilePrefix = filePrefix;

    //get file list from storage to display
    InitializeItemsFromStorage(storageDir, filePrefix);
}

//this method creates an array of files stored on the device or SD card.
private void InitializeItemsFromStorage(File storageDir, String prefix) {

    log("in InitializeItemsFromStorage()");
    mItems.clear();

    File[] files = getFiles(storageDir, prefix);
    for (File f : files) {
        SelfieObject selfie = new SelfieObject(f);
        mItems.add(selfie);
    }

}

public void Update() {

    log("in Update()");
    InitializeItemsFromStorage(mStorageDir, mFilePrefix);
    notifyDataSetChanged();
}

/*
 * return the list of file objects of the given directory that begin with
 * the prefix.
 */
private File[] getFiles(File storageDir, final String prefix) {
    FileFilter fileFilter = new FileFilter() {

        @Override
        public boolean accept(File pathname) {
            if (pathname.isFile() && pathname.getName().startsWith(prefix))
                return true;
            else
                return false;
        }
    };

    File[] result = storageDir.listFiles(fileFilter);
    return result;
}

public int getCount() {

    log("in getCount()");
    return mItems.size();
}

public Object getItem(int position) {

    log("in getItem()");
    return mItems.get(position);
}

public long getItemId(int position) {

    log("in getItemId()");
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    Log.v(TAG, "in getView for position " + position +
            ", convertView is " +
            ((convertView == null)?"null":"being recycled"));

    View newView = convertView;
    ViewHolder holder;

    if (null == convertView) {

        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        newView = inflater.inflate(R.layout.single_item, null);

        holder = new ViewHolder();
        holder.description = (TextView) newView.findViewById(R.id.textView1);
        holder.picture = (ImageView) newView.findViewById(R.id.imageView1);
        newView.setTag(holder);

    } else {
        holder = (ViewHolder) newView.getTag();
    }

    holder.picture.setScaleType(ImageView.ScaleType.CENTER_CROP);

    SelfieObject selfie = (SelfieObject) getItem(position);
    setPic(holder.picture, new Point(WIDTH, HEIGHT), selfie.getPath());

    TextView textView = (TextView) holder.description;
    textView.setText(selfie.getName());     

    log("Exiting getView");
    return newView;
}

static class ViewHolder {

    ImageView picture;
    TextView description;   
}

public void add(SelfieObject listItem) {
    mItems.add(listItem);
    notifyDataSetChanged();
}

public ArrayList<SelfieObject> getList(){
    return mItems;
}

public void removeAllViews(){
    mItems.clear();
    this.notifyDataSetChanged();
}

public static void setPic(ImageView imageView, Point requestedSize,
        String pathName) {
    // set the dimensions of the View
    int targetW = requestedSize.x;
    int targetH = requestedSize.y;

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(pathName, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW / targetW, photoH / targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(pathName, bmOptions);
    imageView.setImageBitmap(bitmap);
    imageView.setRotation(ROTATION);
}

//Automation logging tool
public void log(String s){

    Log.i(TAG, s);
}

private class AsyncTaskLoadImage extends AsyncTask<Object, Void, Bitmap>{

    private ImageView image;
    private String path;

    public AsyncTaskLoadImage(ImageView image){

        this.image = image; 
        this.path = image.getTag().toString();
    }

    @Override
    protected Bitmap doInBackground(Object... params) {

        Bitmap bitmap = null;
        File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + path);

        if(file.exists()){
            bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
        }

        return bitmap;
    }
}

}

Joe C
  • 184
  • 1
  • 15

1 Answers1

1

The AsyncTask should do whatever is too slow to do in the UI thread. In this example, fetching and downsampling the image, and setting up the ViewHolder should be done in the background.

However, I suggest you do not try and fix the ListView by yourself, but rather have a look at already existing solutions, like: https://github.com/lucasr/smoothie

Also, I highly suggest you downsample your bitmaps, otherwise they will consume a lot of excess computing time and memory. While the previous can lag your UI when scrolling, the latter will get you a nice OutOfMemoryException. See: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

zsolt.kocsi
  • 333
  • 2
  • 12
  • I was actually looking at Piccaso for some assistance on loading but I am trying to do everything manually at least once so I can understand what is happening (as a learning experience). I'd have thought my setPic might have done the trick for downsizing my bitmaps. The listview loads the thumbnails fine but i can still easily see 500ms so of delay when loading the list for each new view. I was hoping an AsyncTask would help with that. – Joe C Jan 23 '15 at 04:54