I know that this question is asked many times.. But I cant get any proper solution, In my app I use AsyncTask for loading a image. I use AsyncTask because I want smooth scrolling listview.. and after implement AsyncTask My listView get smoothly scrolling.. But now problem is that Image is changing after scrolling... What should I do?
MyAdapter Class
package com.example.adapter;
public class DisplayAllFeedAdapter extends ArrayAdapter<DisplayAllFeedItem> {
private final List<DisplayAllFeedItem> list;
private final Activity context;
ExifInterface exif;
public DisplayAllFeedAdapter(Activity context, List<DisplayAllFeedItem> list) {
super(context, R.layout.feed_screen, list);
this.context = context;
this.list = list;
}
static class ViewHolder {
public TextView optionalDesc, reportedBy;
public LinearLayout layout;
public ImageView displayFeedimg, channelIcon;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
LayoutInflater inflater = context.getLayoutInflater();
convertView = inflater.inflate(R.layout.display_all_feed_listitem, null);
viewHolder = new ViewHolder();
viewHolder.optionalDesc = (TextView) convertView.findViewById(R.id.txtFeedOptionalDesc);
viewHolder.reportedBy = (TextView) convertView.findViewById(R.id.txtFeedReportedBy);
viewHolder.displayFeedimg = (ImageView) convertView
.findViewById(R.id.imgFeedDisplayImage);
viewHolder.layout = (LinearLayout) convertView.findViewById(R.id.layoutChannelImgView);
convertView.setTag(viewHolder);
convertView.setTag(R.id.txtFeedOptionalDesc, viewHolder.optionalDesc);
convertView.setTag(R.id.txtFeedReportedBy, viewHolder.reportedBy);
convertView.setTag(R.id.imgFeedDisplayImage, viewHolder.displayFeedimg);
convertView.setTag(R.id.layoutChannelImgView, viewHolder.layout);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
String temp;
String drawableImagePath = list.get(position).getMediaChannelName();
List<String> channelList = new ArrayList<String>();
while (drawableImagePath.length() > 0) {
if (drawableImagePath.indexOf(",") == -1) {
temp = drawableImagePath.substring(0);
channelList.add(temp);
break;
} else {
temp = drawableImagePath.substring(0, drawableImagePath.indexOf(","));
}
channelList.add(temp);
drawableImagePath = drawableImagePath.substring(drawableImagePath.indexOf(",") + 1);
}
viewHolder.layout.removeAllViews();
for (int i = 0; i < channelList.size(); i++) {
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(dpToPx(30),
dpToPx(30));
ImageView imageView = new ImageView(context);
layoutParams.setMargins(0, 0, 10, 0);
imageView.setLayoutParams(layoutParams);
imageView.setImageResource(Integer.parseInt(channelList.get(i)));
viewHolder.layout.addView(imageView);
}
String tempPath = list.get(position).getMediaPath();
File mediaFile = new File(tempPath);
Bitmap bitmap;
viewHolder.optionalDesc.setText(list.get(position).getMediaDesc());
viewHolder.reportedBy.setText("Reported By " + list.get(position).getMediaDisplayName());
if (viewHolder != null) {
new LoadImage(viewHolder).execute(tempPath);
}
return convertView;
}
private class LoadImage extends AsyncTask<String, String, Bitmap> {
// File mediaFile = new File(tempPath);
Bitmap bitmap, displayBitmap;
File mediaFile;
String mediaPath;
private final ViewHolder imageViewReference;
public LoadImage(ViewHolder viewHolder) {
imageViewReference = viewHolder;
}
#Override
protected Bitmap doInBackground(String... params) {
// File f = new File(params[0]);
mediaPath = params[0];
mediaFile = new File(mediaPath);
if (mediaFile.exists()) {
if (isImage(mediaPath)) {
Bitmap myBitmap = BitmapFactory.decodeFile(mediaFile.getAbsolutePath());
int height = (myBitmap.getHeight() * 512 / myBitmap.getWidth());
Bitmap scale = Bitmap.createScaledBitmap(myBitmap, 512, height, true);
int rotate = 0;
try {
exif = new ExifInterface(mediaFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
rotate = 0;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
}
Matrix matrix = new Matrix();
matrix.postRotate(rotate);
displayBitmap = Bitmap.createBitmap(scale, 0, 0, scale.getWidth(),
scale.getHeight(), matrix, true);
} else {
displayBitmap = ThumbnailUtils.createVideoThumbnail(mediaPath,
Thumbnails.MICRO_KIND);
}
}
return displayBitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if (imageViewReference != null) {
imageViewReference.displayFeedimg.setImageBitmap(result);
}
// tempView.displayFeedimg.setImageBitmap(result);
}
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return px;
}
public static boolean isImage(String str) {
boolean temp = false;
String[] arr = { ".jpeg", ".jpg", ".png", ".bmp", ".gif" };
for (int i = 0; i < arr.length; i++) {
temp = str.endsWith(arr[i]);
if (temp) {
break;
}
}
return temp;
}
}
I am not clear on your problem (need better clarification) but I know there are issues on loading images about the lag in performance.
Please Look at Google's web Making a Standard Request.
Search for (Universal) Image Loader, text "Use ImageLoader and NetworkImageView".
You can further improve performance by caching, located below that text.
Code snippet from the webpage:
ImageLoader mImageLoader;
ImageView mImageView;
// The URL for the image that is being loaded.
private static final String IMAGE_URL =
"http://developer.android.com/images/training/system-ui.png";
...
mImageView = (ImageView) findViewById(R.id.regularImageView);
// Get the ImageLoader through your singleton class.
mImageLoader = MySingleton.getInstance(this).getImageLoader();
mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,
R.drawable.def_image, R.drawable.err_image));
There seems a good tutorial in Solving the Android image loading problem... by a knowledgeable author.
Have fun with this. And someday I may have to solve this problem as well. Pls keep us posted.
Besides my previous answer, there is another possible solution. But it is somewhat different than Google's recommendation, so beware...
Look at Stackoverflow discussion ListView like in universal image loader sample app. Checkout code in that page. In CarListAdapter:
ImageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
...In another method:
imageLoader.displayImage(...);
Both of my answers lead to one point, use the (Universal) Image Loader.
Related
Hello I am using listview and display images from server into it.
But the problem is that all loaded images are displayed in last item one by one instead display on respective position.
please help me to display that images in respective position
public class Offer_adapter extends ArrayAdapter<String> {
Context context1;
String[] offer_title;
String[] offerimg1;
String[] mrp;
String[] offerprice;
String[] you_save;
String[] imgURLArray;
Bitmap bitmap;
ImageView offerimg;
int a;
LayoutInflater inflater1;
public Offer_adapter(Context context1, String[] offer_title, String[] offerimg1, String[] mrp, String[] you_save, String[] offerprice) {
super(context1, R.id.offer_list, offer_title);
this.context1 = context1;
this.offer_title = offer_title;
this.offerimg1 = offerimg1;
this.mrp = mrp;
this.offerprice = offerprice;
this.you_save = you_save;
}
private static class ViewHolder {
String offerimg;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
inflater1 = (LayoutInflater) context1.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater1.inflate(R.layout.offer_list, null);
viewHolder = new ViewHolder();
}
android.util.Log.v("abhi", "" + position);
imgURLArray = new String[position + 1];
for (int i = 0; i <= position; i++) {
android.util.Log.v("abhijit", "" + position);
imgURLArray[i] = "http://www.surun.co/preost/mod_offer/images/" + offerimg1[position];
android.util.Log.v("abhi", "" + imgURLArray[position]);
}
a=position;
viewHolder = (ViewHolder) convertView.getTag();
TextView offertitle = (TextView) convertView.findViewById(R.id.ofrtitle);
TextView offermrp = (TextView) convertView.findViewById(R.id.offeroriginal);
offermrp.setPaintFlags(offermrp.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
TextView offersave = (TextView) convertView.findViewById(R.id.saveoffer);
TextView ofrprice = (TextView) convertView.findViewById(R.id.priceoffer);
offerimg = (ImageView) convertView.findViewById(R.id.ofr_img);
offertitle.setText(offer_title[position]);
offermrp.setText("Original Price: \u20B9" + mrp[position]);
offersave.setText("You Save: \u20B9" + you_save[position]);
ofrprice.setText("Offer Price: \u20B9" + offerprice[position]);
// Bitmap imageBitmap = null;
new DownloadAsyncTask().execute(imgURLArray[position]);
Log.v("abhi","async");
return convertView;
}
private class DownloadAsyncTask extends AsyncTask<String, String, Bitmap> {
protected Bitmap doInBackground(String... args) {
try {
Log.v("abhi","in do background");
bitmap = BitmapFactory.decodeStream((InputStream) new URL(args[0]).getContent());
bitmap = Bitmap.createScaledBitmap(bitmap, 270, 375, true);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (bitmap !=null) {
offerimg.setImageBitmap(bitmap);
} else {
offerimg.setImageResource(R.drawable.nooffer);
}
}
}
}
All images are shown in last item one after another. I just want to show it on respective position.
you should use lazyloading instead of downloading image as bitmap.
Bitmap will create problem sometime or will give you outofmemory error in some devices
There are lots of image loading library available for android.
Have a look at these
https://github.com/square/picasso
https://github.com/nostra13/Android-Universal-Image-Loader
https://code.google.com/p/android-query/wiki/ImageLoading
https://android.googlesource.com/platform/frameworks/volley
https://github.com/koush/UrlImageViewHelper
https://github.com/novoda/image-loader
I use custom ArrayAdapter to populate listview.
I use Picasso to load images, before loading image I calculate height and width for each image. Thus dynamic ImageView has different height and width.
When I scroll up the listview, everything is smooth. But when I scroll the listview down, list starts to jump, when it comes to the rows with images.
I think, it caused by listview elements recycle, it forgets dynamic imageviews heights and produce this jumping effect when it recalculating imageviews again. I attach my dynamic imageview to Holder, but it doesn't help.
Part of my adapter looks like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ChatMessageElement el = list.get(position);
ViewHolder holder = null;
NewMessagesLabelHolder labelHolder = null;
if (convertView == null) {
convertView = el.getView(inflater, parent);
if (el.isMessage()) {
holder = new ViewHolder();
holder.messageLayout = (RelativeLayout) convertView.findViewById(R.id.message_container);
holder.messageContent = (LinearLayout) convertView.findViewById(R.id.message_content);
holder.bottomIndicator = (LinearLayout) convertView.findViewById(R.id.bottom_indicators);
holder.dateTextView = (TextView) convertView.findViewById(R.id.message_date);
holder.timeAgo = (TextView) convertView.findViewById(R.id.time_ago);
holder.nameTextView = (TextView) convertView.findViewById(R.id.user_name);
convertView.setTag(holder);
}
} else {
if (el.isMessage()) {
holder = (ViewHolder) convertView.getTag();
}
}
if (el.isMessage()) {
Message currentMessage = (Message) el;
drawMessage(holder, currentMessage, position);
}
return convertView;
}
private void drawMessage(ViewHolder holder, Message message, int position) {
String date = message.getCreatedAt();
String formattedDate = Helper.getInstance().formatDate("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "HH:mm", date);
String userName = message.getUserName();
holder.likesLabelImageView.setVisibility(View.GONE);
holder.likesCountTextView.setVisibility(View.GONE);
holder.nameTextView.setText(userName);
holder.dateTextView.setText(formattedDate);
if (message.isLiked()) {
holder.likesCountTextView.setText(Integer.toString(message.getLikesCount()));
holder.likesCountTextView.setVisibility(View.VISIBLE);
holder.likesLabelImageView.setVisibility(View.VISIBLE);
}
List<MessageComponent> messageComponentList;
messageComponentList = message.getMessageComponents();
drawMessageContent(holder, messageComponentList, message);
holder.nameTextView.setTag(position);
holder.avatarImageView.setTag(position);
holder.nameTextView.setOnClickListener(userClickListener);
holder.avatarImageView.setOnClickListener(userClickListener);
// hang empty onLingClickListener to display context menu when
// long click on whole message
holder.nameTextView.setOnLongClickListener(longClickListener);
holder.avatarImageView.setOnLongClickListener(longClickListener);
}
private void drawMessageContent(ViewHolder holder, final List<MessageComponent> messageComponentList, final Message msg) {
holder.messageContent.removeAllViewsInLayout();
int messageComponentListSize = messageComponentList.size();
for (final MessageComponent messageComponent : messageComponentList) {
messageComponentListSize--;
if (messageComponentListSize == 0)
iAmLast = true;
final String type = messageComponent.getType();
if (type.equals(MessageComponent.MESSAGE_COMPONENT_TEXT_TYPE)) {
TextView textView = new TextView(context);
textView.setText(messageComponent.getText());
setViewBackground(textView, msg);
//reset margins for texts, caused by margin changes for images
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.bottomIndicator.getLayoutParams();
params.setMargins(45, -82, 0, 0);
holder.bottomIndicator.setLayoutParams(params);
holder.messageContent.addView(textView);
}
if (type.equals(MessageComponent.MESSAGE_COMPONENT_IMAGE_TYPE)) {
ViewGroup.LayoutParams params = new ActionBar.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
thumbHeight
);
final RoundedImageView imageView = new RoundedImageView(context);
imageView.setPadding(20, 0, 20, 20);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setCornerRadius(15.0f);
mHandler.post(new Runnable() {
#Override
public void run() {
drawPreview(messageComponent, imageView);
}
});
imageView.setLayoutParams(params);
// hang empty onLingClickListener to display context menu when
// long click on whole message
imageView.setOnLongClickListener(longClickListener);
final RelativeLayout mediaContainer = new RelativeLayout(context);
mediaContainer.addView(imageView);
}
}
}
// Calculates restricted dimensions with a maximum of $goal_width by $goal_height
private ImageSize resize_dimensions(float goal_width, float goal_height, float width, float height) {
float ratio = Math.min(goal_width/width, goal_height/height);
int nwidth = Math.round(width*ratio);
int nheight = Math.round(height*ratio);
if(nwidth>nheight*2)
nheight = 400;
if(nheight>nwidth*2)
nwidth = 600;
ImageSize imageSize = new ImageSize(nwidth, nheight);
return imageSize;
}
private void drawPreview(MessageComponent messageComponent, final ImageView imageView) {
String type = messageComponent.getType();
String mediaPath = messageComponent.getMediaPath();
String thumbPath = messageComponent.getThumbPath();
String thumbUrl = messageComponent.getThumbUrl();
String videoThumbPath = messageComponent.getVideoThumbPath();
Uri uri = null;
if (type.equals(MessageComponent.MESSAGE_COMPONENT_IMAGE_TYPE)) {
if (!TextUtils.isEmpty(mediaPath)) {
uri = Uri.parse("file://" + mediaPath);
File file = new File(uri.getPath());
if (file.exists()) {
resizeAndLoadThumbnail(uri, imageView);
return;
}
}
if (!TextUtils.isEmpty(thumbPath)) {
uri = Uri.parse("file://" + mediaPath);
File file = new File(uri.getPath());
if (file.exists()) {
resizeAndLoadThumbnail(uri, imageView);
return;
}
}
if (thumbUrl != null) {
uri = Uri.parse(thumbUrl);
}
if (uri != null) {
resizeAndLoadThumbnail(uri, imageView);
return;
}
}
}
private void resizeAndLoadThumbnail(Uri uri, final ImageView imageView) {
Picasso.with(context).load(uri).into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
ImageSize imgSize = resize_dimensions(900, 900, width, height);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
imgSize.width,
imgSize.height
);
imageView.setLayoutParams(params);
imageView.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
My ListView Containing all songs with album art is very slow and it lags no matter how much i re size the album art Please Help And How To i get the indexable List to work on the songs Title. Android Beginner and Sorry For Bad English.
Thanks For Any Help
public class SongAdapter extends CursorAdapter implements SectionIndexer{
private String mSections = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private final LayoutInflater mInflater;
public SongAdapter(Context context, Cursor c, int textViewResourceId,
List<String> objects) {
super(context, c,textViewResourceId);
mInflater=LayoutInflater.from(context);
}
private Bitmap bitmap = null;
private BitmapDrawable drawable = null;
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView title1 = (TextView) view.findViewById(R.id.titlelist);
TextView artist1 = (TextView) view.findViewById(R.id.artistlist);
ImageView album1 = (ImageView) view.findViewById(R.id.iconlist);
String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
long albumId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));
StringBuilder titleBuild = new StringBuilder();
titleBuild.append(title);
if(titleBuild.length() > 35)
{
titleBuild.setLength(32);
title = titleBuild.toString()+"...";
}
else
{
title = titleBuild.toString();
}
StringBuilder artistBuild = new StringBuilder();
artistBuild.append(artist);
if(artistBuild.length() > 35)
{
artistBuild.setLength(32);
artist = artistBuild.toString()+"...";
}
else
{
artist = artistBuild.toString();
}
final Uri ART_CONTENT_URI = Uri.parse("content://media/external/audio/albumart");
Uri albumArtUri = ContentUris.withAppendedId(ART_CONTENT_URI, albumId);
ContentResolver res = context.getContentResolver();
InputStream in;
try {
if(bitmap != null)
{
bitmap = null;
if(drawable != null)
{
drawable = null;
}
}
in = res.openInputStream(albumArtUri);
bitmap = BitmapFactory.decodeStream(in);
Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, false);
// bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), albumArtUri);
drawable = new BitmapDrawable(context.getResources(), resizedBitmap);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
drawable = (BitmapDrawable) context.getResources().getDrawable(R.drawable.default_artwork);
}
album1.setImageDrawable(drawable);
title1.setText(title);
artist1.setText(artist);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// TODO Auto-generated method stub
LayoutInflater inflater = (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
return inflater.inflate(R.layout.rowlayout, parent, false);
}#Override
public int getPositionForSection(int section) {
// If there is no item for current section, previous section will be selected
for (int i = section; i >= 0; i--) {
for (int j = 0; j < getCount(); j++) {
if (i == 0) {
// For numeric section
for (int k = 0; k <= 9; k++) {
if (StringMatcher.match(String.valueOf(( getItem(j))), String.valueOf(k)))
return j;
}
} else {
if (StringMatcher.match(String.valueOf(getItem(j)), String.valueOf(mSections.charAt(i))))
return j;
}
}
}
return 0;
}
#Override
public int getSectionForPosition(int position) {
return 0;
}
#Override
public Object[] getSections() {
String[] sections = new String[mSections.length()];
for (int i = 0; i < mSections.length(); i++)
sections[i] = String.valueOf(mSections.charAt(i));
return sections;
}
}
According to me, the problem lies here:
in = res.openInputStream(albumArtUri);
bitmap = BitmapFactory.decodeStream(in);
Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, false);
You should be performing the above operations in a background thread rather than on the main thread, otherwise they'll be blocking the main (UI) thread and make everything unresponsive.
Also, instead of loading the full resolution bitmap directly into memory, consider using BitmapFactory.Options to down-sample it first.
Read more here: http://developer.android.com/training/displaying-bitmaps/index.html
You could also consider using this awesome image loading library (supports content provider uris): https://github.com/nostra13/Android-Universal-Image-Loader
I have recently done some tests with display pictures using a custom gallery that i designed using the media queries and mediastore... It worked great but i really need to do something custom.
I don't wish the pictures to be scanned or available in the mediastore hence i would like to have my app scan a directory and create thumbnails and display these thumbnails.
I am finding it really thin on the ground to find any good quality examples to do this.
Can anyone help with a small example.
Here is what i am looking to do.
Pictures are stored in a directory of the sdcard.
Using my custom gallery it would scan this directory but "NOT" using the mediastore
I need to display the contents of the directory but as thumbnails i presume i would need to create this thumbnails first?
Clicking on a thumnail would should the full screen image from my custom gallery.
I suppose i just need a little help in getting the pictures from the the directory considering there are not stored int eh mediastore so i can't use a query. THe other thing that concerns me is that i would need to create the thumbnails for each of the these images (on the fly??) because display the images but at a reduced size i would suspect would be pretty bad for the performance.
Can anyone lend a helping hand?
Thanks in advance
I did exactly the same a while ago. You have to pass a folder name where your images are to setBaseFolder. This method in turn invokes refresh() which - using a FilenameFilter (code not included but is very easy to implement) gets all images named orig_....jpg from that folder and holds it in mFileList. Then we call notifyDataSetChanged() which in turn will trigger getView() for every cell.
Now, in getView() we either fetch a thumbnail bitmap from a cache if we already have it there, otherwise we make a gray placeholder and start a ThumbnailBuilder to create thumbnail resp. get a bitmap from it.
I think you'll have to change the ThumbnailBuilder a bit, because I create quite large "thumbnails" (500x500) as I need the resized images for other purposes too. Also, as I work with photos taken by the camera there is some stuff there, rotating the image according to the exif information. But basicly, ThumbnailBuilder just checks if there already is a thumbnail image (my thumbnail images are placed the same folder but have prefix small_ instead of orig_) - if the thumbnail picture already exists, we get it as a Bitmap and are done, otherwise the image is generated. Finally, in onPostExecute() the bitmap is set to the ImageView.
public class PhotoAdapter extends BaseAdapter {
private Context mContext;
private int mCellSize;
private File mFolder;
private File[] mFileList;
private Map<Object, Bitmap> mThumbnails = new HashMap<Object, Bitmap>();
private Set<Object> mCreatingTriggered = new HashSet<Object>(); // flag that creating already triggered
public PhotoAdapter(Context context, int cellSize) {
mContext = context;
mCellSize = cellSize;
}
#Override
public int getCount() {
if (mFolder == null) {
return 0; // don't do this
} else {
return mFileList.length;
}
}
#Override
public Object getItem(int position) {
return mFileList[position];
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView view = (ImageView)convertView;
if (view == null) {
view = new ImageView(mContext);
view.setLayoutParams(new GridView.LayoutParams(mCellSize, mCellSize));
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
view.setPadding(8, 8, 8, 8);
view.setBackgroundColor(0xFFC6CCD3);
}
Object item = getItem(position);
Bitmap bm = mThumbnails.get(item);
if (bm == null) {
view.setImageBitmap(null);
if (!mCreatingTriggered.contains(item)) {
mCreatingTriggered.add(item);
new ThumbnailBuilder(view, (File)item).execute();
}
} else {
view.setImageBitmap(bm);
}
return view;
}
public void setBaseFolder(File baseFolder) {
if (baseFolder == null) return;
if (!baseFolder.equals(mFolder)) {
releaseThumbnails();
mFolder = baseFolder;
}
refresh();
}
public void refresh() {
if (mFolder == null) {
return;
}
mFileList = mFolder.listFiles(EtbApplication.origImageFilenameFilter);
if (mFileList == null) mFileList = new File[0];
notifyDataSetChanged();
}
public void releaseThumbnails() {
for (Bitmap bm : mThumbnails.values()) {
bm.recycle();
}
mThumbnails.clear();
}
// ------------------------------------------------------------------------------------ Asynchronous Thumbnail builder
private class ThumbnailBuilder extends AsyncTask<Void, Integer, Bitmap> {
private ImageView mView;
private File mFile;
public ThumbnailBuilder(ImageView view, File file) {
mView = view;
mFile = file;
}
#Override
protected Bitmap doInBackground(Void... params) {
Log.d("adapter", "make small image and thumbnail");
try {
return createThumbnail(mFile.getAbsolutePath());
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(Bitmap result) {
if (result != null) {
mView.setImageBitmap(result);
mThumbnails.put(mFile, result);
} else {
mView.setImageResource(R.drawable.ic_launcher);
}
}
/**
* Creates Thumbnail (also rotates according to exif-info)
* #param file
* #return
* #throws IOException
*/
private Bitmap createThumbnail(String file) throws IOException {
File thumbnailFile = new File(file.replace("orig_", "small_"));
// If a small image version already exists, just load it and be done.
if (thumbnailFile.exists()) {
return BitmapFactory.decodeFile(thumbnailFile.getAbsolutePath());
}
// Decode image size
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, bounds);
if ((bounds.outWidth == -1) || (bounds.outHeight == -1))
return null;
int w, h;
if (bounds.outWidth > bounds.outHeight) { // Querformat
w = 500;
h = 500 * bounds.outHeight / bounds.outWidth;
} else { // Hochformat
h = 500;
w = 500 * bounds.outWidth / bounds.outHeight;
}
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 4; // resample -- kleiner aber noch nicht die 500 Pixel, die kommen dann unten
Bitmap resizedBitmap = BitmapFactory.decodeFile(file, opts);
resizedBitmap = Bitmap.createScaledBitmap(resizedBitmap, w, h, true);
ExifInterface exif = new ExifInterface(file);
String orientString = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
int orientation = orientString != null ? Integer.parseInt(orientString) : ExifInterface.ORIENTATION_NORMAL;
int rotationAngle = 0;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) rotationAngle = 90;
if (orientation == ExifInterface.ORIENTATION_ROTATE_180) rotationAngle = 180;
if (orientation == ExifInterface.ORIENTATION_ROTATE_270) rotationAngle = 270;
Matrix matrix = new Matrix();
matrix.setRotate(rotationAngle, (float) resizedBitmap.getWidth() / 2, (float) resizedBitmap.getHeight() / 2);
Bitmap rotatedBitmap = Bitmap.createBitmap(resizedBitmap, 0, 0, w, h, matrix, true);
resizedBitmap.recycle();
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bytes);
thumbnailFile.createNewFile();
FileOutputStream fo = new FileOutputStream(thumbnailFile);
fo.write(bytes.toByteArray());
fo.close();
//new File(file).delete(); // Originalbild löschen
return rotatedBitmap;
}
}
}
I hope the title is not mis-leading. I am trying to implement onRetainNonConfigurationInstance() of an AsyncTask of loading images and am getting this error "Java.lang.RuntimeException:Unable to retain Activity". Here is my code for onRetainNonConfigurationInstance():
public Object onRetainNonConfigurationInstance(){
//final ListView listview = list;
final int count = list.getChildCount();
final LoadedImage[] mylist = new LoadedImage[count];
for(int i = 0; i < count; i++){
final ImageView v = (ImageView)list.getChildAt(i); // getting error here
mylist[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
}
return mylist;
}
Here is the Logcat:
05-18 08:43:15.385: E/AndroidRuntime(28130): java.lang.RuntimeException: Unable to retain activity {com.MyApps.ImageGen/com.MyApps.ImageGen.Wallpapers}: java.lang.ClassCastException: android.widget.LinearLayout
05-18 08:43:15.385: E/AndroidRuntime(28130): at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:2989)
05-18 08:43:15.385: E/AndroidRuntime(28130): at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3100)
05-18 08:43:15.385: E/AndroidRuntime(28130): at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3216)
05-18 08:43:15.385: E/AndroidRuntime(28130): at android.app.ActivityThread.access$1600(ActivityThread.java:132)
Here is my layout with the ListView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:listSelector="#android:color/transparent" >
</ListView>
</LinearLayout>
Here is how I setup the ListView:
private void setupViews() {
list = (ListView)findViewById(android.R.id.list);
list.setDivider(null);
list.setDividerHeight(0);
imageAdapter = new ImageAdapter(getApplicationContext());
list.setAdapter(imageAdapter);
list.setOnItemClickListener(this);
}
my async task and Item Adapter:
class LoadImagesFromSDCard extends AsyncTask<Object, LoadedImage, Object> {
#Override
protected Object doInBackground(Object... params) {
Bitmap bitmap = null;
Bitmap newbitmap = null;
Uri uri = null;
String [] img = {MediaStore.Images.Media._ID};
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)){
cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, img, null, null, null);
} else {
// cursor = getContentResolver().query(MediaStore.Images.Media.INTERNAL_CONTENT_URI, img, null, null, null);
inInternalStorage = true;
}
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int size = cursor.getCount();
if(size == 0){
//Toast.makeText(getApplicationContext(), "There are no Images on the sdcard", Toast.LENGTH_SHORT).show();
//System.out.println("size equals zero");
Wallpaper.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "There are no Images on the sdcard", Toast.LENGTH_SHORT).show();
}
});
return params;
}else {
}
for(int i = 0; i < size; i++){
cursor.moveToPosition(i);
int ImageId = cursor.getInt(column_index);
if(inInternalStorage == true){
uri = Uri.withAppendedPath(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "" + ImageId);
}else {
uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + ImageId);
}
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Log.i(TAG, "imageheight = " + imageHeight);
Log.i(TAG, "imagewidth = " + imageWidth);
Log.i(TAG, "imageType = " + imageType);
//options.inSampleSize=4;
options.inSampleSize = calculateInSampleSize(options, imageWidth, imageHeight);
options.inJustDecodeBounds = false;
//bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
if(bitmap != null){
newbitmap = Bitmap.createScaledBitmap(bitmap, 180, 180, true);
bitmap.recycle();
}
if(newbitmap != null){
publishProgress(new LoadedImage(newbitmap));
}
}catch(IOException e){
}
//cursor.close();
}
cursor.close();
return null;
}
#Override
public void onProgressUpdate(LoadedImage... value){
addImage(value);
}
#Override
protected void onPostExecute(Object result) {
setProgressBarIndeterminateVisibility(false);
}
}
private static class LoadedImage {
Bitmap mBitmap;
LoadedImage(Bitmap bitmap) {
mBitmap = bitmap;
}
public Bitmap getBitmap() {
return mBitmap;
}
}
/*Image Adapter to populate grid view of images*/
public class ImageAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<LoadedImage> photos = new ArrayList<LoadedImage>();
public ImageAdapter(Context context){
this.mContext = context;
}
public void addPhotos(LoadedImage photo){
photos.add(photo);
}
#Override
public int getCount() {
return photos.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView image;
ViewHolder holder;
if(convertView == null){
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//convertView = inflater.inflate(R.layout.image_list,null);
convertView = inflater.inflate(R.layout.media_view, null);
holder = new ViewHolder();
holder.image = (ImageView)convertView.findViewById(R.id.media_image_id);
//holder.item_name = (TextView)convertView.findViewById(R.id.media_image_id);
convertView.setTag(holder);
} else{
holder = (ViewHolder)convertView.getTag();
}
//holder.image.setLayoutParams(new GridView.LayoutParams(100, 100));
//String item_name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));
holder.image.setImageBitmap(photos.get(position).getBitmap());
//holder.item_name.setText(item_name);
return convertView;
}
}
static class ViewHolder {
ImageView image;
TextView item_name;
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Cursor cursor = null;
int image_column_index = 0;
String[] proj = {MediaStore.Images.Media.DATA};
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)){
cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,proj,null, null,null);
}else{
cursor = managedQuery(MediaStore.Images.Media.INTERNAL_CONTENT_URI,proj,null, null,null);
}
cursor.moveToPosition(position);
image_column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
String info = cursor.getString(image_column_index);
Intent imageviewer = new Intent(getApplicationContext(), ViewImage.class);
imageviewer.putExtra("pic_name", info);
startActivity(imageviewer);
cursor.close();
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 2;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
/*final int REQUIRED_SIZE = 180;
int scale = 1;
if(reqWidth > REQUIRED_SIZE || reqHeight > REQUIRED_SIZE){
scale = (int)Math.pow(2, (int)Math.round(Math.log(REQUIRED_SIZE/(double)Math.max(reqHeight, reqWidth)) / Math.log(0.5)));
}*/
return inSampleSize;
}
other Methods within Async Task:
private void loadImages() {
final Object data = getLastNonConfigurationInstance();
if(data == null){
new LoadImagesFromSDCard().execute();
}else {
final LoadedImage[] photos = (LoadedImage[])data;
if(photos.length == 0){
new LoadImagesFromSDCard().execute();
}
for(LoadedImage photo:photos){
addImage(photo);
}
}
}
private void addImage(LoadedImage... value) {
for(LoadedImage photo: value){
imageAdapter.addPhotos(photo);
imageAdapter.notifyDataSetChanged();
}
}
I am assuming I should first get the root element before I get the ListView from the logcat error, but I don't know how, I can't seem to find a suitable method from the documentation.
P.S: I am using an Activity and not a ListActivity.
The exception tells you that you try to do an invalid cast. My guess is that your row layout isn't a simple ImageView like you assume with the cast in the onRetainNonConfigurationInstance(), instead your row layout probably has a parent Linearlayout that wraps the ImageView(and other views?!). When you call the method getChildAt you get that parent Linearlayout which you try to cast to a ImageView. Instead you should do this:
//...
for(int i = 0; i < count; i++){
LinearLayout parent = (LinearLayout)list.getChildAt(i);
final ImageView v = (ImageView) parent.findViewById(R.id.the_id_of_the_imageview_from_theRow_layout);
mylist[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
}
Note: The getChildAt method returns the rows views for the visible rows only so if you try to access the View for a row that isn't currently visible you'll most likely end up with a NullPointerException(you should get the drawables directly from the adapter and not from parsing all the rows Views).
Edit based on the comments:
If I understood your problem, the behavior you see it's normal. You didn't say how you ended up getting the bitmaps in the onRetainNonConfigurationInstance but my guess is that you just save the images from the ListView rows that are currently visible to the user. When it's time to restore the adapter with the images from getLastNonConfigurationInstance() you end up getting only those images which were previously visible. This will get worse if you rotate the phone again as, most likely, there are fewer images visible in landscape orientation then in the portrait orientation.
I don't know if this will work but you could try to modify the LoadedImage class to store the id of the image from MediaStore.Images.Media. When it's time to save the configuration, stop the LoadImagesFromSDCard task and nullify all the images(remove the Bitmap to which LoadedImage points)from the LoadedImages ArrayList(in the adapter) except the ones that are currently visible to the user. Send the photos ArrayList from your adapter through
onRetainNonConfigurationInstance.
When it's time to restore the adapter set the photos ArrayList of your new adapter to the one you got back and put the ListView to the position of the visible images(so the user sees the sent images). In the getView method of your adapter you'll put a default image(like loading...) when you have a null value for the Bitmap in the associated LoadedImage class. Start a new LoadImagesFromSDCard task to get the images and parse them again. When you get the id from MediaStore.Images.Media you'll check if a LoadedImage with this id exists in the adapter and if it has a Bitmap set to it. If it doesn't exist(or it doesn't have a Bitmap associated) then load the image and put it in the adapter, if not the image already exists and move to the next one.
I don't know if the solution above will work or if it's efficient. I would recommend you to modify the code and load images on demand instead of loading all up in a big task.