Chethan Shetty
Chethan Shetty

Reputation: 2070

How to load images effectively and efficiently on scrolling in android

Hello stackoverflow I'm trying to develop an application like Gallery for a particular directory of SD Card. For example I have took WhatsApp Images directory, here is my code

public class WhatsAppInboxImagesActivity extends Activity {

private ImageAdapter imageAdapter;
private ProgressDialog pd;
ArrayList<String> f = null;
File[] listFile;
Button btnDelete;
HashSet<String> selectedFile = null;
GridView imagegrid;
AlertDialog alertDialog = null;

static
{
    File noFile = new File(Environment.getExternalStorageDirectory().getPath(), "/WhatsApp/Media/WhatsApp Images/Sent/.nomedia");
    if(noFile.exists())
    {
        noFile.delete();
    }
}//End of static block

private void initialize()
{
    imagegrid = (GridView) findViewById(R.id.PhoneImageGrid);
    btnDelete = (Button) findViewById(R.id.btnDeleteImg);
    selectedFile = new HashSet<String>();// list of file paths boolean checked
    f = new ArrayList<String>();// list of file paths
}//End of initialize method

@Override
public void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_whats_app_images_inbox);

    this.initialize();
    getFromSdcard();        
    imageAdapter = new ImageAdapter();

    /*
     * Performing time consuming actions
     */
    new AsyncTask<Void, Void, Void>() 
    {
        @Override
        protected void onPreExecute() 
        {
            pd = ProgressDialog.show(WhatsAppInboxImagesActivity.this,
                    "Loading..", "Please Wait", true, false);
        }// End of onPreExecute method

        @Override
        protected Void doInBackground(Void... params) 
        {
            return null;
        }// End of doInBackground method

        @Override
        protected void onPostExecute(Void result) 
        {
            imagegrid.setAdapter(imageAdapter);
            imageAdapter.notifyDataSetChanged();
            pd.dismiss();
        }
    }.execute((Void[]) null);


    btnDelete.setOnClickListener(new View.OnClickListener() 
    {
        @Override
        public void onClick(View v) 
        {
             new AsyncTask<Void, Void, Void>() 
             {
                    @Override
                    protected void onPreExecute() 
                    {
                        pd = ProgressDialog.show(WhatsAppInboxImagesActivity.this,
                                "Loading..", "Please Wait", true, false);
                    }// End of onPreExecute method

                    @Override
                    protected Void doInBackground(Void... params) 
                    {
                        @SuppressWarnings("rawtypes")
                        Iterator iterator = selectedFile.iterator();
                        while (iterator.hasNext())
                        {
                            new File(iterator.next().toString()).delete();
                        }//End of while loop
                        return null;
                    }// End of doInBackground method

                    @Override
                    protected void onPostExecute(Void result) 
                    {
                        pd.dismiss();
                    }//End of onPostExecute method
                }.execute((Void[]) null);

                finish();
                startActivity(getIntent());
        }//End of onClick method
    });//End of btnDelete anonymous class

}//End of onCreate method

public void getFromSdcard() 
{
    File file = new File( Environment.getExternalStorageDirectory().getPath(), "/WhatsApp/Media/WhatsApp Images");

    if (file.isDirectory()) 
    {
        listFile = file.listFiles();
        for (int i = 0; i < listFile.length; i++) 
        {
            if(listFile[i].isDirectory())
            {
                continue;
            }
            f.add(listFile[i].getAbsolutePath());
        }//End of for loop
    }//End of if condition
}//End of getFromSdcard method

public class ImageAdapter extends BaseAdapter 
{
    private LayoutInflater mInflater;

    public ImageAdapter() 
    {
        mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }//End of ImageAdapter constructor

    public int getCount() 
    {
        return f.size();
    }//End of getCount method

    public Object getItem(int position) 
    {
        return position;
    }//End of getItem method

    public long getItemId(int position) 
    {
        return position;
    }//End of getItemId method

    public View getView(int position, View convertView, ViewGroup parent) 
    {
        ViewHolder holder;
        if (convertView == null) 
        {
            holder = new ViewHolder();
            convertView = mInflater.inflate(R.layout.galleryitem, null);
            holder.imageview = (ImageView) convertView.findViewById(R.id.thumbImage);
            holder.checkbox = (CheckBox) convertView.findViewById(R.id.itemCheckBox);

            convertView.setTag(holder);
        }//End of if condition
        else 
        {
            holder = (ViewHolder) convertView.getTag();
        }//End of else

        BitmapFactory.Options options = new BitmapFactory.Options();
        // will results in a much smaller image than the original
        options.inSampleSize = 4;

        // don't ever use a path to /sdcard like this, but I'm sure you have a sane way to do that
        // in this case nebulae.jpg is a 19MB 8000x3874px image
        final Bitmap b = BitmapFactory.decodeFile(f.get(position), options);

        holder.imageview.setImageBitmap(b);

        return convertView;
    }//End of getView method
}//End of ImageAdapter instance inner class

class ViewHolder 
{
    ImageView imageview;
    CheckBox checkbox;
    int id;
}//End of ViewHolder instance inner class
}//End of WhatsAppImagesActivity

So far every thing is good, but the problem is images gets loaded all at one shot, so scrolling is very slow and some times OutOfMemoryException occurs though decoded file with BitMapFactory, can any one help me to solve this puzzle please.

Edit:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View view = null;
    if (convertView == null) {
        LayoutInflater inflator = context.getLayoutInflater();
        view = inflator.inflate(R.layout.list_layout, null);
        final ViewHolder viewHolder = new ViewHolder();
        viewHolder.text = (TextView) view.findViewById(R.id.label);
        viewHolder.text.setTextColor(Color.BLACK);
        viewHolder.image = (ImageView) view.findViewById(R.id.image);
        viewHolder.image.setVisibility(View.GONE);
        viewHolder.cb = (CheckBox) view.findViewById(R.id.item_check_box);
        viewHolder.pb = (ProgressBar) view.findViewById(R.id.progressBar);
        view.setTag(viewHolder);
    } else {
        view = convertView;
    }
    final int pos = position;
    ViewHolder holder = (ViewHolder) view.getTag();
    holder.text.setText(list.get(position).getName());
    holder.image.setTag(list.get(position).getURL());
    holder.image.setId(position);
    PbAndImage pb_and_image = new PbAndImage();
    pb_and_image.setImg(holder.image);
    pb_and_image.setPb(holder.pb);
    new DownloadImageTask().execute(pb_and_image);
    return view;
}

Thanks.

Upvotes: 0

Views: 4120

Answers (2)

RRTW
RRTW

Reputation: 3190

This could help: UniversalImageLoader

And that's what I did:

/**
 * UniversalImageLoader載入遠端圖片,僅使用RAM做圖片快取 (Load pic & use RAM as cache)
 * @param android.content.Context
 * @author https://github.com/nostra13/Android-Universal-Image-Loader
 * @param android.widget.ImageView - 顯示圖片的元件 (your image container)
 * @param int - 載入失敗的預設圖片Resource ID (default pic if load fail, I put default pic in resources folder)
 * @param java.lang.String - 檔案URL (pic URL or path)
 */
public static void imageLoaderMemoryCache(Context context, final ImageView img, final int failImgID, String url)
{
  ImageLoader imageLoader=ImageLoader.getInstance();
  ImageLoaderConfiguration config=new ImageLoaderConfiguration.Builder(context)
    .memoryCacheExtraOptions(100, 100) // max width, max height
    .threadPoolSize(5)
    .threadPriority(Thread.NORM_PRIORITY+1)
    .denyCacheImageMultipleSizesInMemory()
    .tasksProcessingOrder(QueueProcessingType.FIFO)
    .defaultDisplayImageOptions(DisplayImageOptions.createSimple())
    .enableLogging()
    .build();
  imageLoader.init(config);

  DisplayImageOptions options = new DisplayImageOptions.Builder()
    .showImageForEmptyUri(R.drawable.cover_sample)
    .cacheInMemory(true)
    .imageScaleType(ImageScaleType.IN_SAMPLE_INT)
    .bitmapConfig(Bitmap.Config.RGB_565)
    .delayBeforeLoading(1)
    .displayer(new FadeInBitmapDisplayer(500))
    .build();

  imageLoader.displayImage(url, img, options, new ImageLoadingListener()
  {
    @Override
    public void onLoadingStarted(String url, View view)
    {img.setImageResource(failImgID);} //一開始先顯示預設圖 (display default pic at first)
    @Override
    public void onLoadingFailed(String url, View view, FailReason failReason)
    {img.setImageResource(failImgID);}
    @Override
    public void onLoadingComplete(String url, View view, Bitmap loadedImage)
    {}
    @Override
    public void onLoadingCancelled(String url, View view)
    {}
  });
}

Usage (ListView)

public View getView(...)
{
  vh.imgCover=(ImageView)convertView.findViewById(R.id.imgCover);
  //...
  // some stuff
  //...
  Util.imageLoaderDiskCache(getActivity(), vh.imgCover, R.drawable.cover_sample, "(pic file path)");
}

Hope it helps~

Upvotes: 0

Praveen Sharma
Praveen Sharma

Reputation: 4348

You should use Lazy loading

visit this tutorial for lazy loading here Its very easy. Let me know if you have any issue on it.

Edit

public class MainActivity extends ListActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    String path = Environment.getExternalStorageDirectory()+"/WhatsApp/Media/WhatsApp Images";

    final List<Model> list = new ArrayList<Model>();
    final File file = new File(path);

    final ProgressDialog dialog = ProgressDialog.show(MainActivity.this, "",
            "Please Wait...", true);
    new Thread(new Runnable() {
        public void run() {

            dismissDialog(dialog);
            for(File fileChild : file.listFiles()){
                list.add(get(fileChild.getName(), fileChild.getAbsolutePath()));
            }

        }
    }).start();

    ArrayAdapter<Model> adapter = new MyCustomArrayAdapter(this, list);
    setListAdapter(adapter);
}

public void dismissDialog(final ProgressDialog dialog) {
    runOnUiThread(new Runnable() {
        public void run() {
            dialog.dismiss();
        }
    });
}

private Model get(String s, String url) {
    return new Model(s, url);
}

}

Upvotes: 3

Related Questions