0

I am trying to implement an activity shown in the image(Wireframe) uploaded(). There could be n number of such items.(Lazy Loading is expected to be implemented.) Images are ImageButtons and QTY(quantity) are spinners. Item name, item price and item images are fetched from server.

I expect a different implementation when I click on Item Image and When I click on qunatity spinner. This is not possible when I implement this using listview + adapter. (I have followed http://wptrafficanalyzer.in/blog/android-lazy-loading-images-and-text-in-listview-from-http-json-data/)

I expect the images will be loaded in a different thread.

Could anyone please give me a direction on how to go about this? I have read enough implementations using listview + adapters. None of them help solve the problem Expected wireframe design

user1051505
  • 952
  • 3
  • 22
  • 37
  • http://stackoverflow.com/questions/15621936/whats-lazylist. Inflate a custom layout in your adapter and use UIL or lazy list for image – Raghunandan May 08 '13 at 16:35
  • Inflate a custom layout in your adapter. Do this using listview and adapter right? How would I differentiate between the imagebutton click and spinner click? (Since both would be considered as a single listitem click. Hope I am clear on this.) – user1051505 May 08 '13 at 16:41
  • you can have click listeners for your views inside getview() – Raghunandan May 08 '13 at 16:44
  • @Raghunandan Consider, I clicked on 2 item image. How would I know for which ItemId this event has occurred? With the listview I have public void onItemClick(AdapterView> arg0, View v, int position,long id) which would give me which item is clicked with position. But whether I press item image or spinner. I would run into the same event. How do I distinguish between the 2? – user1051505 May 08 '13 at 17:15
  • you have the position for that purpose – Raghunandan May 08 '13 at 17:16
  • Sorry, just edited what I had to say. – user1051505 May 08 '13 at 17:17

2 Answers2

1

Lars has a great tutorial on ListViews and using custom adapters to populate them here. The section titled "ListViews and performance" should be the most helpful.

For the lazy loading, check out Universal Image Loader. All you have to do is initialize the image loader in your application class and then call ImageLoader.getInstance() anywhere you need to use it. Using .displayImage() takes an image uri and an ImageView. All the threading and bitmap decoding is done by the library. It makes it absurdly easy and has tons of extra features.

Wenger
  • 989
  • 2
  • 12
  • 35
  • Loading images is not a problem to me. If I click on ImageButton or Spinner, this will be taking me to onItemClick(AdapterView> arg0, View v, int position,long id). Tell me how do I differentiate between imagebutton click and spinner click. – user1051505 May 08 '13 at 17:30
  • Done! Actually I was working with SimpleAdapter. I had to make my own custom adapter extending base adapter and do the necessary in getview() as suggested by @raghunandan. Thanks a lot! – user1051505 May 09 '13 at 11:47
  • Sorry I didn't clarify that more in the answer. Using `BaseAdapter` and working in `getView()` is really powerful for creating your own custom lists. For performance though, you want to keep your `getView()` as simple as possible. – Wenger May 09 '13 at 12:02
1

// File cache class

package com.imageloader;

import java.io.File;
import android.content.Context;

public class FileCache {

    private File cacheDir;

    public FileCache(Context context){
        //Find the dir to save cached images
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }

    public File getFile(String url){
        //I identify images by hashcode. Not a perfect solution, good for the demo.
        String filename=String.valueOf(url.hashCode());
        //Another possible solution (thanks to grantland)
        //String filename = URLEncoder.encode(url);
        File f = new File(cacheDir, filename);
        return f;

    }

    public void clear(){
        File[] files=cacheDir.listFiles();
        if(files==null)
            return;
        for(File f:files)
            f.delete();
    }

}

// ImageLoader class

package com.imageloader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;

public class ImageLoader {

    MemoryCache memoryCache=new MemoryCache();
    FileCache fileCache;
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService; 

    public ImageLoader(Context context){
        fileCache=new FileCache(context);
        executorService=Executors.newFixedThreadPool(5);
    }

    final int stub_id = R.drawable.ic_launcher;
    public void DisplayImage(String url, ImageView imageView)
    {
        imageViews.put(imageView, url);
        Bitmap bitmap=memoryCache.get(url);
        if(bitmap!=null)
            imageView.setImageBitmap(bitmap);
        else
        {
            queuePhoto(url, imageView);
            imageView.setImageResource(stub_id);
        }
    }

    private void queuePhoto(String url, ImageView imageView)
    {
        PhotoToLoad p=new PhotoToLoad(url, imageView);
        executorService.submit(new PhotosLoader(p));
    }

    private Bitmap getBitmap(String url) 
    {
        File f=fileCache.getFile(url);

        //from SD cache
        Bitmap b = decodeFile(f);
        if(b!=null)
            return b;

        //from web
        try {
            Bitmap bitmap=null;
            URL imageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setInstanceFollowRedirects(true);
            InputStream is=conn.getInputStream();
            OutputStream os = new FileOutputStream(f);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex){
           ex.printStackTrace();
           return null;
        }
    }

    //decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f){
        try {
            //decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(f),null,o);

            //Find the correct scale value. It should be the power of 2.
            final int REQUIRED_SIZE=70;
            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2;
            }

            //decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize=scale;
            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        } catch (FileNotFoundException e) {}
        return null;
    }

    //Task for the queue
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public PhotoToLoad(String u, ImageView i){
            url=u; 
            imageView=i;
        }
    }

    class PhotosLoader implements Runnable {
        PhotoToLoad photoToLoad;
        PhotosLoader(PhotoToLoad photoToLoad){
            this.photoToLoad=photoToLoad;
        }

        public void run() {
            if(imageViewReused(photoToLoad))
                return;
            Bitmap bmp=getBitmap(photoToLoad.url);
            memoryCache.put(photoToLoad.url, bmp);
            if(imageViewReused(photoToLoad))
                return;
            BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
            Activity a=(Activity)photoToLoad.imageView.getContext();
            a.runOnUiThread(bd);
        }
    }

    boolean imageViewReused(PhotoToLoad photoToLoad){
        String tag=imageViews.get(photoToLoad.imageView);
        if(tag==null || !tag.equals(photoToLoad.url))
            return true;
        return false;
    }

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        PhotoToLoad photoToLoad;
        public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
        public void run()
        {
            if(imageViewReused(photoToLoad))
                return;
            if(bitmap!=null)
                photoToLoad.imageView.setImageBitmap(bitmap);
            else
                photoToLoad.imageView.setImageResource(stub_id);
        }
    }

    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }

}

// MemoryCache Class

package com.imageloader;

import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import android.graphics.Bitmap;

public class MemoryCache {
    private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());

    public Bitmap get(String id){
        if(!cache.containsKey(id))
            return null;
        SoftReference<Bitmap> ref=cache.get(id);
        return ref.get();
    }

    public void put(String id, Bitmap bitmap){
        cache.put(id, new SoftReference<Bitmap>(bitmap));
    }

    public void clear() {
        cache.clear();
    }
}

// Utils class

package com.imageloader;

import java.io.InputStream;
import java.io.OutputStream;

public class Utils {
    public static void CopyStream(InputStream is, OutputStream os)
    {
        final int buffer_size=1024;
        try
        {
            byte[] bytes=new byte[buffer_size];
            for(;;)
            {
              int count=is.read(bytes, 0, buffer_size);
              if(count==-1)
                  break;
              os.write(bytes, 0, count);
            }
        }
        catch(Exception ex){}
    }
}

// Getting images from url in BaseAdapter for listview and GridView

package com.inflater;

import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.globaldata.Utilities;
import com.imageloader.ImageLoader;

public class MyAdapter extends BaseAdapter {

    private Context mContext;
    private ImageLoader mImageLoader;
    private ArrayList<Model> mArrayList = new ArrayList<Model>();
    private LayoutInflater mLayoutInflater;

    public VideoArrangeAdapter(Context ctx, ArrayList<Model> mData) {

        this.mContext = ctx;
        this.mArrayList = mData;
        this.mImageLoader = new ImageLoader(this.mContext);
        this.mLayoutInflater = (LayoutInflater) this.mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {

        return mArrayList.size();
    }

    @Override
    public Object getItem(int position) {

        return mArrayList.get(position);
    }

    @Override
    public long getItemId(int arg0) {

        return arg0;
    }

    private class ViewHolder {
        private ImageView iv_image;
        private TextView textview_title;

    }

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

        ViewHolder holder = null;

        if (convertView == null) {

            holder = new ViewHolder();
            convertView = mLayoutInflater.inflate(R.layout.item_of_listview,
                    null);
            holder.iv_image = (ImageView) convertView
                    .findViewById(R.id.iv_image);
            holder.textview_title = (TextView) convertView
                    .findViewById(R.id.textview_title);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.textview_title.setText(mArrayList.get(position).get_title());
        mImageLoader.DisplayImage(mArrayList.get(position).get_thumb(),
                holder.iv_image);

        notifyDataSetChanged();

        return convertView;

    }

}
Hiren Patel
  • 52,124
  • 21
  • 173
  • 151