Provide Only bitmap to Glide Not url - android

i have list of video Urls. i create thumbnails from them. These thumbnails are in form of bitmap.so what i tried
Glide.with(mContext)
.load(bitmap)
.into(mVideoThumbnail)
What i found from Here.We can do something like this.
Glide.with(mContext)
.load(url).asBitmap()
.into(mVideoThumbnail)
But above function is used for loading URL as bitmap. It doesn't take bitmap as parameter.
i also know that i can set directly bitmap into image as below mentioned
mVideoThumbnail.setImageBitmap(bitmap);
Above method works fine if i have to set thumbnail for single video but in case of multiple videos, it cause some performance issue.
i am sharing my code for fetching thumbnail as a bitmap and set into my ImageView. Is There any way to pass directly bitmap to Glide or any other options are available for reducing performance issue. Please Help
public class TopicInstructionViewHolder implements View.OnClickListener {
#BindView(R.id.iv_thumbnail)
ImageView mVideoThumbnail;
#BindView(R.id.iv_play_video)
ImageView mVideoPlayIcon;
#BindView(R.id.tv_instruction_name)
TextView mInstructionName;
private ITopicVideoPlayListener mTopicVideoPlayListener;
private Context mContext;
private String videoPath;
private int instructionId;
private boolean mHasVideoSeenBL;
public TopicInstructionViewHolder(View itemView,
ITopicVideoPlayListener mTopicVideoPlayListener,
Context mContext) {
ButterKnife.bind(this, itemView);
this.mTopicVideoPlayListener = mTopicVideoPlayListener;
this.mContext = mContext;
}
public void setData(TopicInstructionDetail topicInstructionDetail) {
String thumbnailPath = null;
TopicInstructionTranslationDetail topicInstructionTranslationDetails = findTopicInstructionAsPerLang(topicInstructionDetail.getmTopicInstructionTranslationDetails());
mVideoPlayIcon.setOnClickListener(this);
videoPath = topicInstructionTranslationDetails.getmInstructionPath();
mHasVideoSeenBL = topicInstructionDetail.isCompleteSeen();
instructionId = topicInstructionTranslationDetails.getmInstructionId();
mInstructionName.setText(topicInstructionTranslationDetails.getmInstructionName());
thumbnailPath = (NetworkConstants.VIDEO_URL + topicInstructionTranslationDetails.getmThumbnailPath());
new SampleAsyncTask().execute(NetworkConstants.VIDEO_URL+videoPath);
if (topicInstructionDetail.isCompleteSeen()) {
mVideoPlayIcon.setImageResource(R.drawable.check);
} else {
mVideoPlayIcon.setImageResource(R.drawable.ic_play);
}
}
private TopicInstructionTranslationDetail findTopicInstructionAsPerLang(List<TopicInstructionTranslationDetail> topicInstructionTranslationDetails) {
TopicInstructionTranslationDetail topicInstructionTranslationDetail = null;
for (TopicInstructionTranslationDetail topicTranslation : topicInstructionTranslationDetails) {
if (topicTranslation.getmLanguage().equals(AppPreferencesHelper.getInstance(mContext).getCurrentUserLanguage())) {
topicInstructionTranslationDetail = topicTranslation;
}
}
if (topicInstructionTranslationDetail == null) {
topicInstructionTranslationDetail = findDefaultTopicInstruction(topicInstructionTranslationDetails);
}
return topicInstructionTranslationDetail;
}
private TopicInstructionTranslationDetail findDefaultTopicInstruction(List<TopicInstructionTranslationDetail> topicInstructionTranslationDetails) {
TopicInstructionTranslationDetail topicInstructionDetail = null;
for (TopicInstructionTranslationDetail topicTranslation : topicInstructionTranslationDetails) {
if (topicTranslation.getmLanguage().equals(LanguageCode.getLanguageCode(LanguageCode.LANGUAGE_FIRST))) {
topicInstructionDetail = topicTranslation;
}
}
return topicInstructionDetail;
}
#Override
public void onClick(View view) {
mTopicVideoPlayListener.playVideo(videoPath, instructionId, mHasVideoSeenBL);
}
//fetching bitmap from video url
private class SampleAsyncTask extends AsyncTask {
#Override
protected Bitmap doInBackground(String... strings) {
Bitmap bitmap = null;
MediaMetadataRetriever mediaMetadataRetriever = null;
try {
mediaMetadataRetriever = new MediaMetadataRetriever();
if (Build.VERSION.SDK_INT >= 14) {
mediaMetadataRetriever.setDataSource(strings[0], new HashMap<String, String>());
} else {
mediaMetadataRetriever.setDataSource(strings[0]);
}
bitmap = mediaMetadataRetriever.getFrameAtTime(1, MediaMetadataRetriever.OPTION_CLOSEST);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mediaMetadataRetriever != null) {
mediaMetadataRetriever.release();
}
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap s) {
super.onPostExecute(s);
try {
mVideoThumbnail.setImageBitmap(s);
/* Glide.with(mContext)
.load(s).asBitmap()
.into(mVideoThumbnail);*/
}
catch (Exception e){
e.printStackTrace();
}
}
}
}

You are using Glide v3. Glide v4 did add an option for loading Bitmap as Drawable. Docs here
Update to v4 require some more configuration, check here

Use Fresco Library instead of Glide.
You can use facebook fresco library instead of glide for smooth and efficient loading in list. Fresco library have pipeline techniques for loading the image thumbnail in list. Use Asynctask and fresco pipeline to get the Thumbnail from video and load it.
Check this answer, implemented solution for your problem.

Related

What is the right coding about Image Loading?

I'm solving my problem about Image Loader and I have some problems..
What I want is to show many images (about 400) in GridView(or ListView).
I don't want to use the Library like Picasso, Glide like that.
and Here is the problem.
When I call the method which convert from url to bitmap?
3.1. before setAdapter, then pass the bitmap array.
3.2. while getView.
two things are working well. but too much slow... maybe cuz of the times to call URLConnection..
Could anyone help me about these problem? How can I speed up? or are there any other solution without Open Source.
Here is my Source.
Now, 3-1.
ShowImage
private void showImages(ArrayList<String> imgUrls) {
ArrayList<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < imgUrls.size(); i++) {
try {
String img_path = imgUrls.get(i);
Bitmap bitmap = new UriToBitmapAsyncTask().execute(img_path).get();
bitmaps.add(bitmap);
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
CustomAdapter adapter = new CustomAdapter(getApplicationContext(),R.layout.row,bitmaps);
gridView.setAdapter(adapter);
}
and This is the customAdapter's GetView
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflator.inflate(rowLayout, parent, false);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.imageView.setImageBitmap(bitmaps.get(position));
return convertView;
}
You should really take Reinventing the wheel to heart but if you really want to toture yourself an Approach could be:
use a ThreadPoolExecutor to fetch more images at once, you should read up how to use them
implement a way to cancel threads who load a img for a griditem which isn't displayed anymore
use two sets of data a thumbnail which loads faster for the grid view and a real image which gets loaded when the user clicks on the grid
dont't forget to use a LRU caching method or your device will run out of memory depending on the images
Don't use ArrayList to store bitmaps. Bitmaps usually take consumes a lot of memory. Try using LRUCache like this way,
public class TCImageLoader implements ComponentCallbacks2 {
private TCLruCache cache;
public TCImageLoader(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
int maxKb = am.getMemoryClass() * 1024;
int limitKb = maxKb / 8; // 1/8th of total ram
cache = new TCLruCache(limitKb);
}
public void display(String url, ImageView imageview, int defaultresource) {
imageview.setImageResource(defaultresource);
Bitmap image = cache.get(url);
if (image != null) {
imageview.setImageBitmap(image);
}
else {
new SetImageTask(imageview).execute(url);
}
}
private class TCLruCache extends LruCache<String, Bitmap> {
public TCLruCache(int maxSize) {
super(maxSize);
}
#Override
protected int sizeOf(ImagePoolKey key, Bitmap value) {
int kbOfBitmap = value.getByteCount() / 1024;
return kbOfBitmap;
}
}
private class SetImageTask extends AsyncTask<String, Void, Integer> {
private ImageView imageview;
private Bitmap bmp;
public SetImageTask(ImageView imageview) {
this.imageview = imageview;
}
#Override
protected Integer doInBackground(String... params) {
String url = params[0];
try {
bmp = getBitmapFromURL(url);
if (bmp != null) {
cache.put(url, bmp);
}
else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
return 0;
}
return 1;
}
#Override
protected void onPostExecute(Integer result) {
if (result == 1) {
imageview.setImageBitmap(bmp);
}
super.onPostExecute(result);
}
private Bitmap getBitmapFromURL(String src) {
try {
URL url = new URL(src);
HttpURLConnection connection
= (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
}
#Override
public void onLowMemory() {
}
#Override
public void onTrimMemory(int level) {
if (level >= TRIM_MEMORY_MODERATE) {
cache.evictAll();
}
else if (level >= TRIM_MEMORY_BACKGROUND) {
cache.trimToSize(cache.size() / 2);
}
}
}
get a instance of TCImageLoader and call display method appropriately.

How to load Media Metadata Smoothly in a RecyclerView

I have a Music class that needs to return its cover art as a bitmap in order to use it on a RecylerView. I am using an AsyncTask inner class in the class to perform the retrieval, however, my app freezes once the list is being created from cover arts. Please see the code below for Music.java:
public class Music {
private static final String LOG_TAG = Music.class.getSimpleName();
private String mId;
private String mTitle;
private String mUrl;
private Bitmap mCoverArt;
public Music(String id, String title, String url) {
mId = id;
mTitle = title;
mUrl = url;
mCoverArt = null; //Initialize with null
}
String getId() {
return mId;
}
String getTitle() {
return mTitle;
}
String getUrl() {
return mUrl;
}
Bitmap getCoverArt() {
if(mCoverArt != null) {
return mCoverArt;
}
else {
Bitmap bmp = null;
try {
bmp = new GetCoverArt().execute(mUrl).get();
} catch (InterruptedException e) {
Log.e(LOG_TAG, "InterruptedException: " + e.getMessage());
} catch (ExecutionException e) {
Log.e(LOG_TAG, "ExecutionException: " + e.getMessage());
}
return bmp;
}
}
public void setCoverArt(Bitmap bmp) { mCoverArt = bmp; }
private static class GetCoverArt extends AsyncTask<String, Void, Bitmap> {
#Override
protected Bitmap doInBackground(String... paths) {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(paths[0], new HashMap<String,String>());
byte[] picData = mmr.getEmbeddedPicture();
return BitmapFactory.decodeByteArray(picData, 0, picData.length);
}
}
}
I am calling getCoverArt() in onBindViewHolder for my RecyclerView this way:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Music song = mDataset.get(position);
Bitmap songCoverArt = song.getCoverArt();
String songTitle = song.getTitle();
String songId = song.getId();
String songUrl = song.getUrl();
if(songCoverArt != null) {
Glide.with(mContext).load(songCoverArt).into(holder.coverArt);
}
else {
holder.coverArt.setImageResource(R.drawable.coverart_fallback);
}
Bitmap bmp = song.getCoverArt();
if(bmp != null) {
Glide.with(mContext).load(bmp).into(holder.coverArt);
}
else {
Glide.with(mContext).load(R.drawable.coverart_fallback).into(holder.coverArt);
}
I do not understand why doInBackground in AsyncTask might cause the UI thread to freeze. I thought it all runs in the background, but it seems my RecyclerView is waiting for it to finish the job before it can use the value returned. Currently, as an a bad workaround, I am doing such processing in another AsyncTask in the main activity along with other network operations when I construct Music objects and add them to an ArrayList:
for( int j = 0 ; j < songs.length() ; j++) {
JSONObject song = songs.getJSONObject(j); //get song at index j
String songId = song.getString( getString(R.string.json_song_id) );
String title = song.getString( getString(R.string.json_song_title));
String path = song.getString( getString(R.string.json_filepath) );
//Create a temp Music object to extract Music info
Music songObj = new Music(songId, title, path);
Bitmap bmp = createCoverArtBmp(path);
songObj.setCoverArt(bmp);
mMusicTrackArray.add(songObj); //Add the music object into array
}
You don't need to convert it to bitmap from the url to show the images. Simply pass the image url to Glide and it will load it for you.
By calling bitmap making in the getCoverArt() you are not doing it async but waiting for the task to finish as a result freezing your view.
See here how to use Glide with url : https://github.com/bumptech/glide#how-do-i-use-glide
#Umar Hussain is correct with passing urls, but Glide can also use local files or URI's doing so is covered in:
Glide load local image by Uri.
The benefit here of using local files (I suggest saving to your cache) is that you don't have to pass around bulky bitmaps. Out of memory exceptions make developers sad.
I have noticed you are also using fallbacks from if statements, but Glide has a placeholder method
Glide.with(getContext())
.load(some_bitmap_file_url_or_drawable)
.placeholder(some_placeholder_drawable)
.into(view_you_want_it_to_appear);
This should transition to the image you want when it loads, plus provide a fallback if it doesn't

Volley + Picasso: Is it possible to use Volley as downloader for Picasso and its image cache structure

Hello StackOverflowers,
I developed project which is using Volley for communicating with REST web API. I use complex image loading process.
I want to load user's facebook profile picture and sometimes I want it first to get storage cached image and sometimes I want it to be always from internet.
The problem is that image url from facebook is always the same. This is why I made a basic check if image file size differs from the cached one.
I was using Volley's ImageLoader and it's cache implementation. Then I found it complex to do it with ImageLoader so I choose to use Picasso but it doesn't show cached image.
I know that for Picasso cache to work I need a custom "Downloader" and I know I can do it with OkHttp but my project is already using Volley for all REST calls.
So is it possible to use Volley as downloader for Picasso and its image cache structure?
Here is how I managed to fix the first issue and only the caching flow is the problem now:
public class PerfectImageLoader {
private static final String TAG = PerfectImageLoader.class.getSimpleName();
private Context mContext;
private SharedPreferences mSharedPreferences;
public PerfectImageLoader(Context context)
throws NullPointerException {
if (context == null) {
throw new NullPointerException("Context cannot be null");
}
mContext = context;
mSharedPreferences = context.getSharedPreferences(
context.getPackageName(), Context.MODE_PRIVATE);
}
public void getImage(final String imageUrl, final ImageView imageView) {
getImage(imageUrl, imageView, false);
}
public void getImage(final String imageUrl, final ImageView imageView,
final boolean isAwlaysFromInternet)
throws NullPointerException {
if (imageView == null) {
throw new NullPointerException("OnImageFromCacheListener cannot be null");
}
if (TextUtils.isEmpty(imageUrl)) {
throw new NullPointerException("image url cannot be null");
}
if (!isAwlaysFromInternet) {
loadCachedImage(imageUrl, imageView, null);
}
calculateFileSize(imageUrl, new OnFileSizeCheck() {
#Override
public void ready(final int networkFileSize) {
int cachedImageSize = mSharedPreferences
.getInt(imageUrl, 0);
TLog.v(TAG, "networkFileSize:" + networkFileSize);
TLog.v(TAG, "cachedImageSize:" + cachedImageSize);
if (cachedImageSize != networkFileSize || cachedImageSize == 0) {
TLog.v(TAG, "cachedImageSize != networkFileSize");
final Callback callback = new Callback() {
#Override
public void onSuccess() {
TLog.v(TAG, "downloaded");
mSharedPreferences.edit()
.putInt(imageUrl, networkFileSize).apply();
}
#Override
public void onError() {
TLog.v(TAG, "error");
if (isAwlaysFromInternet) {
mSharedPreferences.edit()
.remove(imageUrl).apply();
imageView.setImageBitmap(null);
}
}
};
if (isAwlaysFromInternet) {
TLog.v(TAG, "MemoryPolicy.NO_CACHE");
Picasso.with(mContext).load(imageUrl).memoryPolicy(MemoryPolicy.NO_CACHE)
.into(imageView, callback);
} else {
Picasso.with(mContext).load(imageUrl).into(imageView, callback);
}
} else {
TLog.v(TAG, "cachedImageSize == networkFileSize");
loadCachedImage(imageUrl, imageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(mContext).load(imageUrl).into(imageView);
}
});
}
}
});
}
private void loadCachedImage(final String imageUrl, final ImageView imageView,
Callback callback) {
if (callback != null) {
Picasso.with(mContext)
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView, callback);
} else {
Picasso.with(mContext)
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView);
}
}
public static void calculateFileSize(String url, final OnFileSizeCheck fileSizeCallbacks)
throws NullPointerException {
if (fileSizeCallbacks != null && !TextUtils.isEmpty(url)) {
new AsyncTask<String, Void, Integer>() {
#Override
protected Integer doInBackground(String... params) {
Integer fileSize = null;
try {
URL urlObj = new URL(params[0]);
URLConnection urlConnection = urlObj.openConnection();
urlConnection.connect();
fileSize = urlConnection.getContentLength();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fileSize;
}
#Override
protected void onPostExecute(Integer fileSize) {
super.onPostExecute(fileSize);
if (fileSizeCallbacks != null) {
fileSizeCallbacks.ready(fileSize);
}
}
}.execute(url);
} else {
if (fileSizeCallbacks == null) {
throw new NullPointerException("file size callbacks parameter is null");
}
if (url == null) {
throw new NullPointerException("url parameter is null");
} else if (TextUtils.isEmpty(url)) {
throw new NullPointerException("url parameter is empty");
}
}
}
}

How to save images downloaded via UrlImageViewHelper to SD card on Android?

I'm using UrlImageViewHelper library and it works fine.
It caches the images on internal storage and it's bad for me because I've about lots of images and if I want to cache them it's horrible.
How can I save these downloaded files and store them in SD card instead of internal storage?
Please check your whether your URL is valid or not by putting into browser. If image size is large then please use placeholder image which still displays your URL, not load image available, like this:
UrlImageViewHelper.setUrlDrawable(imageView, "http://example.com/image.png", R.drawable.placeholder);
Use this class
public class DemoHelper {
private static final String TAG = DemoMainActivity.TAG;
public static class RemoteImageTask extends AsyncTask<Void, Void, Bitmap> {
ImageView _image;
String _imageSource;
TaskCallback _callback;
public RemoteImageTask(ImageView image, String imageSource) {
this(image, imageSource, null);
}
public RemoteImageTask(ImageView image, String imageSource, TaskCallback callback) {
_image = image;
_imageSource = imageSource;
_callback = callback;
}
protected Bitmap doInBackground(Void... params) {
URL url;
Bitmap bmp = null;
try {
url = new URL(_imageSource);
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (Exception ignored) {
Log.e(TAG, "Exception", ignored);
}
return bmp;
}
protected void onPostExecute(Bitmap bmp) {
_image.setImageBitmap(bmp);
if (_callback != null)
_callback.onTaskFinished(bmp);
}
}
public interface TaskCallback {
public void onTaskFinished(final Bitmap bmp);
}
}

How to efficiently store bitmaps in Android?

I'm building a relatively basic news-reader app that involves displaying news in a custom listview (Image + Title + Short Description per list element).
My question is How can I store the images I download from the server and then attach them to the listview? The images will be relatively small, 200 X 200 usually, in .jpeg format.
It's not so much a question of how as much as "how to do it efficiently", as I'm already noticing lag in lower-end phones when using the default "ic_launcher" icon instead of bitmaps.
Would it be faster to store them as files or into the news database along with other news data when the app starts and syncs up the news or cache them...?
How should I go about this?
better you can do it's use SoftReference via an ImageManager class.
In you ListAdpater getView() method call the displayImage() method of ImageManager.
ImageManager Coding Exemple :
public class ImageManagerExemple {
private static final String LOG_TAG = "ImageManager";
private static ImageManagerExemple instance = null;
public static ImageManagerExemple getInstance(Context context) {
if (instance == null) {
instance = new ImageManagerExemple(context);
}
return instance;
}
private HashMap<String, SoftReference<Bitmap>> imageMap = new HashMap<String, SoftReference<Bitmap>>();
private Context context;
private File cacheDir;
private ImageManagerExemple(Context context) {
this.context = context;
// Find the dir to save cached images
String sdState = android.os.Environment.getExternalStorageState();
if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
File sdDir = android.os.Environment.getExternalStorageDirectory();
cacheDir = new File(sdDir,"data/yourappname");
} else {
cacheDir = context.getCacheDir();
}
if(!cacheDir.exists()) {
cacheDir.mkdirs();
}
}
/**
* Display web Image loading thread
* #param imageUrl picture web url
* #param imageView target
* #param imageWaitRef picture during loading
*/
public void displayImage(String imageUrl, ImageView imageView, Integer imageWaitRef) {
String imageKey = imageUrl;
imageView.setTag(imageKey);
if(imageMap.containsKey(imageKey) && imageMap.get(imageKey).get() != null) {
Bitmap bmp = imageMap.get(imageKey).get();
imageView.setImageBitmap(bmp);
} else {
queueImage(imageUrl, imageView);
if(imageWaitRef != null)
imageView.setImageResource(imageWaitRef);
}
}
private void queueImage(String url, ImageView imageView) {
ImageRef imgRef=new ImageRef(url, imageView);
// Start thread
Thread imageLoaderThread = new Thread(new ImageQueueManager(imgRef));
// Make background thread low priority, to avoid affecting UI performance
imageLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
imageLoaderThread.start();
}
private Bitmap getBitmap(String url) {
String filename = String.valueOf(url.hashCode());
File f = new File(cacheDir, filename);
try {
// Is the bitmap in our cache?
Bitmap bitmap = BitmapFactory.decodeFile(f.getPath());
if(bitmap != null) return bitmap;
// Nope, have to download it
bitmap = ImageServerUtils.pictureUrlToBitmap(url);
// save bitmap to cache for later
writeFile(bitmap, f);
return bitmap;
} catch (Exception ex) {
ex.printStackTrace();
Log.e(LOG_TAG, ""+ex.getLocalizedMessage());
return null;
} catch (OutOfMemoryError e) {
Log.e(LOG_TAG, "OutOfMemoryError : "+e.getLocalizedMessage());
e.printStackTrace();
return null;
}
}
private void writeFile(Bitmap bmp, File f) {
if (bmp != null && f != null) {
FileOutputStream out = null;
try {
out = new FileOutputStream(f);
//bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
bmp.compress(Bitmap.CompressFormat.JPEG, 80, out);
} catch (Exception e) {
e.printStackTrace();
}
finally {
try { if (out != null ) out.close(); }
catch(Exception ex) {}
}
}
}
private class ImageRef {
public String imageUrl;
public ImageView imageView;
public ImageRef(String imageUrl, ImageView i) {
this.imageUrl=imageUrl;
this.imageView=i;
}
}
private class ImageQueueManager implements Runnable {
private ImageRef imageRef;
public ImageQueueManager(ImageRef imageRef) {
super();
this.imageRef = imageRef;
}
#Override
public void run() {
ImageRef imageToLoad = this.imageRef;
if (imageToLoad != null) {
Bitmap bmp = getBitmap(imageToLoad.imageUrl);
String imageKey = imageToLoad.imageUrl;
imageMap.put(imageKey, new SoftReference<Bitmap>(bmp));
Object tag = imageToLoad.imageView.getTag();
// Make sure we have the right view - thread safety defender
if (tag != null && ((String)tag).equals(imageKey)) {
BitmapDisplayer bmpDisplayer = new BitmapDisplayer(bmp, imageToLoad.imageView);
Activity a = (Activity)imageToLoad.imageView.getContext();
a.runOnUiThread(bmpDisplayer);
}
}
}
}
//Used to display bitmap in the UI thread
private class BitmapDisplayer implements Runnable {
Bitmap bitmap;
ImageView imageView;
public BitmapDisplayer(Bitmap b, ImageView i) {
bitmap=b;
imageView=i;
}
#Override
public void run() {
if(bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
}
The trick to getting smooth ListView scrolling without stutter is to not update it in any way, shape or form while the user is scrolling it. Afaik, this is essentially how iOS manages to get its ListViews that smooth: it disallows any changes to it (and the UI in general) while the user has his finger on it.
Just comment out any code that changes your ListView while leaving all the bitmap loading code intact, and you'll see that the actual loading of the bitmaps in the background doesn't really impact performance at all. The problem is that the UI thread can't keep up with view updates and scrolling at the same time.
You can achieve the same thing by using a OnScrollListener that blocks all updates to the ListView while the User is scrolling it. As soon as the user stops, you can sneak in all pending updates.
For added performance, try not to use notifyDataSetChanged but iterate over the views of the ListView and only update the views that have actually changed.

Categories

Resources