Reputation: 4257
I want to show a listview with some texts and images. When i'm creating a view for listview, i'm calling method show of my PictureImageView, that downloads and showing image. Download is running in new thread in AsyncTask. But while image downloading i can't normally scroll listview, it's twitches. To run AsyncTask in new thread i call executeOnExecutor method. I tried to call execute method, but then scroll stops at all till download is over.
Here my class.
public class PictureImageView extends LinearLayout {
private Drawable image_drawable = null;
private ImageView image = null;
...
protected String getImageURL() {
...
return uri;
}
public void show() {
if (image_drawable != null) {
image.setImageDrawable(image_drawable);
addView(image);
} else {
// target Android API >= 14 so executeOnExecutor works in another thread
new RequestTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getImageURL());
}
}
protected void onResponse(Drawable image) {
if (image != null) {
image_drawable = image;
show();
}
}
class RequestTask extends AsyncTask<String, String, Drawable> {
@Override
protected Drawable doInBackground(String... urls) {
Drawable image = null;
HttpURLConnection connection = null;
InputStream connection_stream = null;
try {
URL url = new URL(urls[0]);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setUseCaches(true);
connection.connect();
int response_code = connection.getResponseCode();
//@see http://libs-for-android.googlecode.com/svn/reference/com/google/android/filecache/FileResponseCache.html
if (response_code == HttpURLConnection.HTTP_OK || response_code == -1) {
connection_stream = connection.getInputStream();
image = Drawable.createFromStream(connection_stream, null);
}
} catch (MalformedURLException e) {
} catch (IOException e) {
} finally {
if (connection != null) {
connection.disconnect();
}
if (connection_stream != null) {
try {
connection_stream.close();
} catch (IOException e) {
}
}
}
return image;
}
@Override
protected void onPostExecute(Drawable image) {
PictureImageView.this.onResponse(image);
}
}
}
How can i fix it? I guess, the problem is that there is no any another thread, but how to check it?
Upvotes: 1
Views: 602
Reputation: 7663
I've delt with this exact problem first hand. The twitching comes from updating the ListView
each time a picture is downloaded. There are 2 different approaches I took to fix this. Depending on your project set up one my work
Approach 1: Minimize twitching by only updating once
In my case I used an AsyncTask
as a seperate class with a call back to the starting activity. What I did was use a singleThreadExecutor
so that the task to download each user's picture were serialy executed and a counter to track how many treads were started/left - increamenting each time I added one to the executor, decrementing each time the call back was called. For example
@Override
public void userPic(Bitmap pic){
if(pic != null){
//use picture
}
taskCounter--
if(taskCounter == 0){
updateUserListView();
}
}
By updating once all threads were done I was able to minimize the twitching by only refreshing the list once, thus allowing scroll and jumping back to the top only once all picutres were done
Approach 2: eliminate twitch by using mem cache
Eventually what I ened up doing was using a cache to store bitmaps. This approach completely eliminated the jumping issue beacuse the list was no longer being refreshed, rather the adapter was loading bitmaps from the cache only when views were recycled. I still used a seperate task with a call back
@Override
public void userPic(Bitmap pic){
if(pic != null){
memCache.addPicture(pic);
}
}
only this time rather than update the list directly, if a picture was downloaded I stored it to the cache. Then in my adapter code, I set the picutre field to update from cache if present
if(picture_view != null){
if(memCache.contains(u.getId()){
picture_view.setImageBitmap(memCache.getPicture(u.getId()));
} else {
picture_view.setImageBitmap(memCache.getPicture("default"));
}
this approach takes advatage of the fact that views are updated in a ListView
automaticaly once they are recycled. As you scroll and the views are rebuilt, the adapter will automatically populate the fields with new data if it has changed.
Downsides - the list does not auto upate. If pictures are downloaded for fields that are currently visible, they will not be updated until you scroll away from that view. Also, slightly more set up in creating a cache. I chose to use a singelton pattern to do this since I was accessing the cache from multiple places (e.g. adding pictures in one place and getting in another).
Upvotes: 1