OutOfMemoryError caused by multiple (>120) calls of getView in GridView - android

I am using a GridView to display images. The images are downloaded from a feed and added to a BitmapCache. The GridView is inside of a ViewFlipper (which has a ListView as second View). I'm using GridView for the first time but I've worked with Adapters many times when I used ListViews.
At the moment, the feed only delivers two images. But when I start my Fragment containing the GridView I get an OutOfMemoryError caused bei BitmapFactory.decodeStream(). When I took a deeper look into the logcat, I noticed that getView() inside of my Adapter for the GridView is called many many times.
I know that it's nothing special if getView() is called more than once, but the getView()-method in my Adapter gets called over 120 times only for position 0. And I don't really understand why it's called so often. But I'm pretty sure that this caused my memory problem as this method tries to load a bitmap over 100 times in just a few seconds.
As I'm already trying to recycle my view with a ViewHolder I'm quite helpless at the moment and I hope somebody can explain me this massive calls of getView() and/or might give me a hint to solve my problem.
The getView()-mthod of my adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.pictures_grid_item, parent, false);
holder.image = (ImageView) convertView.findViewById(R.id.picturesGridImage);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
holder.image.setImageBitmap(null);
}
Picture picture = (Picture) pictureList.get(position);
String imageUrl = picture.getUrl();
if (!TextUtils.isEmpty(imageUrl)) {
holder.image.setTag(imageUrl);
ImageLoader.getInstance(context).loadImageWithTagCheck(holder.image);
}
return convertView;
}
private static class ViewHolder {
ImageView image;
}
The loadImageWithTagCheck()-method just checks if the image has already been downloaded (which deffinitely should be the case)
The Fragment which holds the View:
public class PicturesFragment extends BaseFragment {
private List<Parcelable> pictureList;
private PicturesGridAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.pictures_fragment, container, false);
// TODO: Remove final after development
final MediaActivity activity = (MediaActivity) getActivity();
pictureList = activity.getPictures();
adapter = new PicturesGridAdapter(activity, pictureList);
GridView gridview = (GridView) view.findViewById(R.id.picturesGrid);
gridview.setAdapter(adapter);
gridview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(activity, "" + position, Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
BTW: I'm not using *wrap_content* anywhere.
Edit:
Here's the code of the imageloader. Ofcourse, the ImageLoader is the problem which causes the outOfMemoryError. But I think that the problem is rather something with the adapter because 120 calls of getView() for position 0 just after creating the view can't be right. And the Adapter is just created once so it's >120 calls in a single instance of my adapter.
(this is a pretty huge and complex project so the "simple" imageloader has a lot of code)
public void loadImageWithTagCheck(final ImageView view) {
final String url = (String) view.getTag();
final Handler uiHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
}
};
if (imageHandler != null) {
imageHandler.post(new Runnable() {
#Override
public void run() {
final Bitmap bmp = getImage(url, view);
uiHandler.post(new Runnable() {
#Override
public void run() {
String tagUrl = (String) view.getTag();
if (tagUrl.equals(url) && bmp != null
&& !bmp.isRecycled()) {
scaleBitmapAndAdjustViewByHeight(view, bmp);
} else if (bmp != null) {
bmp.recycle();
}
}
});
}
});
}
}
private Bitmap getImage(String url, View v) {
Bitmap bmp = null;
if (url != null && !TextUtils.isEmpty(url)) {
String md5Url = Utility.md5(url);
if (cache.containsKey(md5Url)) {
bmp = cache.getBitmap(md5Url);
} else {
HttpGet httpGet = new HttpGet();
HttpClient httpClient = new DefaultHttpClient();
HttpResponse response = null;
try {
URI uri = new URI(url);
httpGet.setURI(uri);
response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
if (entity != null) {
final BufferedInputStream buffIn = new BufferedInputStream(
entity.getContent(), Utils.IO_BUFFER_SIZE);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.outWidth = v.getWidth();
options.outHeight = v.getHeight();
options.inPurgeable = true;
options.inInputShareable = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
bmp = BitmapFactory.decodeStream(buffIn, null,
options);
}
}
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
if (bmp != null) {
cache.put(md5Url, bmp);
}
}
}
return bmp;
}
private void scaleBitmapAndAdjustViewByHeight(final ImageView view,
final Bitmap bmp) {
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.getViewTreeObserver().removeOnGlobalLayoutListener(
this);
} else {
view.getViewTreeObserver().removeGlobalOnLayoutListener(
this);
}
// Get current dimensions
int width = bmp.getWidth();
int height = bmp.getHeight();
// Determine how much to scale: the dimension requiring less
// scaling is closer to the its side. This way the image always
// stays inside your bounding box AND either x/y axis touches
// it.
int imageViewHeightFromXMLinPixels = view.getHeight();
float xScale = (float) ((imageViewHeightFromXMLinPixels * 2.75) / width);
float yScale = ((float) imageViewHeightFromXMLinPixels)
/ height;
float scale = (xScale <= yScale) ? xScale : yScale;
// Create a matrix for the scaling and add the scaling data
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
// Create a new bitmap
Bitmap scaledBitmap = Bitmap.createBitmap(bmp, 0, 0, width,
height, matrix, true);
width = scaledBitmap.getWidth(); // re-use
view.setImageBitmap(scaledBitmap);
view.getLayoutParams().width = width;
}
});
view.requestLayout();
}

Get rid of the scaleBitmapAndAdjustViewByHeight(...) method.
Instead, do a simple view.setImageBitmap(bmp).
Why?
scaleBitmapAndAdjustViewByHeight(...) calls view.requestLayout() which probably leads to calling your adapters getView(...) and ends in a deadlock and finally the OutOfMemoryError.

Related

Android: To get a image from server

I used a ListView which contains a ImageView and a TextView. I want to get a image from server. I debug the code below, which told me that I can get the image successful, but when return, the image disappeared. I don't know why. I am the new begginner of android. Hope anyone can help me.
The ListView Adapter class:
public View getView(int position, View convertView, ViewGroup parent) {
News news = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId, null);
newsImage = (ImageView) view.findViewById(R.id.news_image);
newsTitle = (TextView) view.findViewById(R.id.news_title);
newsTitle.setText(news.getTitle());
bitmap = HttpUtil.sendImageRequest(news.getImageUrl());
newsImage.setImageBitmap(ImageUtility.ImageSolution(bitmap, newsImage));
return view;
}
the sendImageRequest method:
public static Bitmap sendImageRequest(final String address){
final Bitmap[] bitmap = new Bitmap[1];
new Thread(new Runnable() {
#Override
public void run() {
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) new URL(address).openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
InputStream in = connection.getInputStream();
bitmap[0] = BitmapFactory.decodeStream(in);
} catch (java.io.IOException e) {
e.printStackTrace();
} finally {
if (connection != null){
connection.disconnect();
}
}
}
}).start();
return bitmap[0];
}
the ImageSolution method:
public static Bitmap ImageSolution (Bitmap bitmap, ImageView imageView){
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int newWidth = imageView.getWidth();
int newHeight = imageView.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
height, matrix, true);
return resizedBitmap;
}
the address is http://p2.zhimg.com/10/7b/107bb4894b46d75a892da6fa80ef504a.jpg.
Thank you so much.
You can use a third party library named Glide , which is recommended by google . To add this library , add the the following dependency in your build.gradle file , in module app .
dependencies {
compile 'com.github.bumptech.glide:glide:3.5.2'
compile 'com.android.support:support-v4:22.0.0'
}
And then you can simply load image in following way
Glide.with(context)
.load(news.getImageUrl())
.into(newsImage);
bitmap = HttpUtil.sendImageRequest(news.getImageUrl());
bitmap will always be empty as it is an image request. The function starts a thread but returns immediately. So returns an empty bitmap.
newsImage.setImageBitmap(ImageUtility.ImageSolution(bitmap, newsImage));
If you then try to set that empty bitmap to an imageview things go wrong.
Redesign your app. Only when the thread finishes there is a valid bitmap. And then you can use it.
Why don't you use the Picasso library?
It's really easy to use
Picasso.with(context).load(news.getImageUrl()).into(newsImage);
And it provides caching and image transformations.
Make a viewHolder class inside adapter class,
private static class ViewHolder {
public TextView newsTitle = null;
public ImageView newsImage = null;
}
then code your getView like,
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder itemHolder;
View view = convertView;
if (view == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
itemHolder = new ViewHolder();
view = inflater.inflate(layoutResID, parent, false);
itemHolder.newsImage = (ImageView) view.findViewById(R.id.news_image);
itemHolder.newsTitle = (TextView) view.findViewById(R.id.news_title);
view.setTag(itemHolder);
} else {
itemHolder = (viewHolder) view.getTag();
}
News news = getItem(position);
itemHolder.newsTitle.setText(news.getTitle());
bitmap = HttpUtil.sendImageRequest(news.getImageUrl());
itemHolder.newsImage.setImageBitmap(ImageUtility.ImageSolution(bitmap, newsImage));
return view;
}
or, you can use picasso, it is easy to use...
add this line inside dependencies,
compile 'com.squareup.picasso:picasso:2.5.0'
and for loading image code for loading image will be,
Picasso.with(context).load(news.getImageUrl()).into(itemHolder.newsImage);

Gridview wrong image when scrolling

I am developing an Android application for tablet which will be used as a catalog of many products (around 10 000).
I display them in a gridview, I only display the 250 first, and if I reach the end of these 250 I load 250 more.
My problem is the pictures. My application seems to work when I scroll not quickly, it is smooth, and there is no problems but it can use 300 or 400 MB of RAM, so I guess I'm doing something badly. I heard that I'm supposed to use WeakReferences but I'm not sure how to use them... That was for the first "problem". (It is not really important since the user may only use this application, and if it works very well... But yes, if I can improve it I will try)
The other big problem here is that when I do something like :
Scroll fast to the bottom
Immediately scroll fast to the top
Some pictures of the "bottom" are displayed on the pictures of the "top" (and not temporarly, they change again only if I scroll and come back at them).
What I know actually :
The mechanism of the griview reuse the views. If I have 10 views visibles, When the 5 first are not visibles, they are used for the 5 next ones that will be visible. I don't know how many view are in this "recycler".
This means that when I scroll to the bottom I see the 5 views which were not visible in the place of the 5 next ones for a few moment then the good views are loaded. To not have this bad effect, I set a picture white (the color of my background) using the method setRecyclerListener().
I don't "recycle" / use the good way to load my pictures and recycle them but don't know how to make it better
I use animations, I don't think there is any link with my problem due to them
I load the pictures in AsyncTask, that seems to work, but the picures are always loaded (in fast scroll too) which is not really a good thing
When scrolling bottom then top quickly sometimes I have the wrong pictures displayed. It may happen in other cases.
public class ProduitsAdapter extends ArrayAdapter<Article> {
private final Activity context;
private ArrayList<Article> params;
private Resources res;
static class ViewHolder {
public TextView numero;
public TextView design;
public ImageView image;
public ImageSwitcher imgSwitcher;
public ImageView codeBarre;
public TextView codeBarreText;
public TextView colisage;
public TextView col3;
public TextView col4;
public TextView col5;
public String image_string = "";
public boolean found = true;
public ImageView nouveaute;
}
public ProduitsAdapter(Activity context, ArrayList<Article> params) {
super(context, R.layout.list_single, params);
res = Catalogue.activity.getResources();
this.context = context;
this.params = params;
}
public int getCount() {
return params.size();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = context.getLayoutInflater();
ViewHolder viewHolder = new ViewHolder();
if (Catalogue.type_aff.equals("list")) {
rowView = inflater.inflate(R.layout.list_single, null);
viewHolder.numero = (TextView) rowView
.findViewById(R.id.numero);
viewHolder.design = (TextView) rowView
.findViewById(R.id.designation);
viewHolder.col3 = (TextView) rowView.findViewById(R.id.col3);
viewHolder.col4 = (TextView) rowView.findViewById(R.id.col4);
viewHolder.col5 = (TextView) rowView.findViewById(R.id.col5);
} else {
rowView = inflater.inflate(R.layout.fragment_grid_item, null);
viewHolder.numero = (TextView) rowView
.findViewById(R.id.numero);
viewHolder.design = (TextView) rowView
.findViewById(R.id.designation);
viewHolder.colisage = (TextView) rowView
.findViewById(R.id.grid_colisage);
viewHolder.codeBarre = (ImageView) rowView
.findViewById(R.id.code_barre);
viewHolder.codeBarreText = (TextView) rowView
.findViewById(R.id.code_barre_text);
viewHolder.nouveaute = (ImageView) rowView
.findViewById(R.id.img_nouveaute);
}
viewHolder.image = (ImageView) rowView.findViewById(R.id.img);
rowView.setTag(viewHolder);
}
ViewHolder holder = (ViewHolder) rowView.getTag();
if (Catalogue.type_aff.equals("list")) {
holder.col3.setText(params.get(position).getColonne_pref(3));
holder.col4.setText(params.get(position).getColonne_pref(4));
holder.col5.setText(params.get(position).getColonne_pref(5));
} else {
if (params.get(position).isNouveaute()) {
holder.nouveaute.setImageResource(R.drawable.nouveaute);
holder.nouveaute.setVisibility(ImageView.VISIBLE);
} else
holder.nouveaute.setVisibility(ImageView.GONE);
holder.colisage.setText(params.get(position).getColisage());
if (ProduitsFragment.barcodeVisible) {
holder.codeBarreText.setText(params.get(position)
.getGencodNegoce());
}
}
holder.numero.setText(params.get(position).getCodeArt());
holder.design.setText(params.get(position).getDesignation_pref());
// This is how I store my pictures, because finding one picture in a folder of 10 000 pictures takes too much time
String pattern_deb = holder.numero.getText() + "";
String chemin = Environment.getExternalStorageDirectory().getPath()
+ res.getString(R.string.chemin_app_data)
+ MainActivity.listDossiersSocietes
.get(MainActivity.societeSelected)
+ res.getString(R.string.chemin_dossier_photos);
for (int i = 0; i < pattern_deb.length() - 2; i++) {
chemin += "/" + pattern_deb.charAt(i);
}
chemin += "/";
File f = new File(chemin + params.get(position).getFichierPhoto());
if (f.exists()) {
holder.image_string = f.getAbsolutePath();
new loadImagesFromFileThread(holder, position).execute();
if (!Catalogue.type_aff.equals("list")
&& ProduitsFragment.barcodeVisible)
new loadBarCodeThread(holder, position).execute();
} else {
holder.image.setImageResource(R.drawable.notfound300);
}
return rowView;
}
private BitmapDrawable loadImagesFromFile(ViewHolder holder, int position) {
holder.found = true;
File bmp = new File(holder.image_string);
if (bmp.exists() && !bmp.isDirectory()) {
if (position < ProduitsFragment.gridView.getFirstVisiblePosition()
&& position > ProduitsFragment.gridView
.getLastVisiblePosition()) {
holder.found = false;
return null;
} else {
if (holder.image.getVisibility() == ImageView.VISIBLE) {
holder.found = false;
return null;
}
}
Bitmap myBitmap = BitmapFactory.decodeFile(bmp.getAbsolutePath());
int width = myBitmap.getWidth();
int height = myBitmap.getHeight();
int newWidth = 200;
int newHeight = 200;
if (Catalogue.type_aff.equals("list")) {
newWidth = 40;
newHeight = 40;
} else {
if (Catalogue.nbGrid == 5) {
newWidth = 200;
newHeight = 200;
}
if (Catalogue.nbGrid == 3) {
newWidth = 300;
newHeight = 300;
}
}
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(myBitmap, 0, 0, width,
height, matrix, true);
BitmapDrawable bmd = new BitmapDrawable(context.getResources(),
resizedBitmap);
return bmd;
} else {
holder.found = false;
return null;
}
}
private class loadImagesFromFileThread extends
AsyncTask<Void, Void, BitmapDrawable> {
ViewHolder holder;
int position;
public loadImagesFromFileThread(ViewHolder holder, int position) {
this.holder = holder;
this.position = position;
this.holder.image.setVisibility(ImageView.INVISIBLE);
}
#Override
protected BitmapDrawable doInBackground(Void... params) {
return loadImagesFromFile(holder, position);
}
#Override
protected void onPostExecute(BitmapDrawable bmd) {
if (ProduitsFragment.isNotScrolling) {
if (bmd != null) {
holder.image.setImageDrawable(bmd);
if (holder.image.getVisibility() != ImageView.VISIBLE
&& !(position < ProduitsFragment.gridView
.getFirstVisiblePosition() && position > ProduitsFragment.gridView
.getLastVisiblePosition())) {
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
fadeIn.setDuration(500);
fadeIn.setStartOffset(500);
holder.image.startAnimation(fadeIn);
holder.image.setVisibility(ImageView.VISIBLE);
}
} else {
if (position < ProduitsFragment.gridView
.getFirstVisiblePosition()
&& position > ProduitsFragment.gridView
.getLastVisiblePosition()) {
Log.e("DEBUG", "...");
} else {
if (holder.image.getVisibility() != ImageView.VISIBLE) {
if (!holder.found) {
if (Catalogue.type_aff.equals("list")) {
holder.image
.setImageResource(R.drawable.notfound40);
} else {
if (Catalogue.nbGrid == 5) {
holder.image
.setImageResource(R.drawable.notfound200);
}
if (Catalogue.nbGrid == 3) {
holder.image
.setImageResource(R.drawable.notfound300);
}
}
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
fadeIn.setDuration(500);
holder.image.startAnimation(fadeIn);
holder.image.setVisibility(ImageView.VISIBLE);
} else {
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
fadeIn.setDuration(500);
holder.image.startAnimation(fadeIn);
}
holder.image.setVisibility(ImageView.VISIBLE);
}
}
}
}
}
}
}
I would appreciate any help to correct the problem of the wrong pictures, and if anyone can explain me how to load them properly with some code if possible (without using 300 MB of RAM) that would be really nice.

How to get height of view inside adapter for creating sized bitmap?

I use custom CursorAdapter with custom items.
I need height of view to resize Bitmap from assets folder and set this resized bitmap to ImegeView in list item;
#Override
public void bindView(View view, final Context context, final Cursor cursor) {
final ViewHolder holder = (ViewHolder) view.getTag();
final int imgCol = cursor.getColumnIndex(TableOdelice.COLUMN_URL);
int titleCol = cursor.getColumnIndex(TableOdelice.COLUMN_TITRE);
final int themeCol = cursor.getColumnIndex(TableOdelice.COLUMN_THEME);
String tempPath = getPath(cursor.getString(themeCol), cursor.getString(imgCol));
final String path = tempPath.replace(".", "c.");
String[] arr = cursor.getString(titleCol).split("\\*");
holder.title.setText(arr[0]);
holder.subTitle.setText(arr[1]);
if (itemHeight > 0) {
showThumb(itemHeight, holder.img, path);
} else {
final ImageView v = holder.mainImage;
v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
itemHeight = v.getHeight();
showThumb(itemHeight, holder.img, path);
}
});
}
}
private void showThumb(int height, final ImageView iv, final String path) {
if (thumbs.containsKey(path)) {
iv.setImageBitmap(thumbs.get(path));
} else {
InputStream is = null;
try {
is = context.getAssets().open(path);
} catch (IOException e) {
Log.d("no file at path", path);
e.printStackTrace();
}
if (is != null) {
Bitmap btm = btmHelper.scaleToHeight(BitmapFactory.decodeStream(is);, height);
thumbs.put(path, btm);
iv.setImageBitmap(btm);
}
}
}
For getting view height I use OnGlobalLayoutListener() of view.
But it's very slow ...
Any ideas?
I find answer for my question.
Using this construction I get a correct width or height of view inside adapter for each view.
final ImageView v = holder.mainImage;
v.post(new Runnable() {
#Override
public void run() {
itemHeight = v.getHeight();
Log.d("Height", "" + itemHeight);
}
});
}
Maybe it will help somebody :)
Just give it a try may be it will be faster
v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
itemHeight = v.getHeight();
showThumb(itemHeight, holder.img, path);
v.getViewTreeObserver().removeGlobalOnLayoutListener( ActivityInsatance);
}
});
If you have top or bottom margins, a bug will appear. When you will scroll this listview, a height of each row will increase. In case of v.getViewTreeObserver().addOnGlobalLayoutListener it will grow infinitely. So, my solution follows.
holder.layout.post(new Runnable() {
#Override
public void run() {
int height = holder.layout.getMeasuredHeight(); // Total row height.
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) holder.textView.getLayoutParams();
holder.textView.setHeight(height - lp.topMargin - lp.bottomMargin);
});
where holder.layout is a link to root RelativeLayout of an adapter's row.

Android gridview by using AsyncTask image loading issue

I am new to android. I am displaying images in gridview by using AsyncTask. But there are some issues like:
1: onScroll AsyncTask tasks gets call again.
2: Images are not displaying correctly ( Once I scroll it shows top image and load the original latter).
here is my code:
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) ctx
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ListRowHolder listRowHolder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.ll_sponsor_list_item,
parent, false);
listRowHolder = new ListRowHolder();
listRowHolder.imgSponsor = (ImageView) convertView
.findViewById(R.id.imggrid_item_image);
convertView.setTag(listRowHolder);
} else {
listRowHolder = (ListRowHolder) convertView.getTag();
}
try {
task = new BitmapWorkerTask(listRowHolder.imgSponsor);
task.image_path = ImageName.get(position);
task.execute(1);
} catch (Exception e) {
if (thisLogger != null) {
thisLogger.error(e.toString());
thisLogger.error("\r\n");
}
}
return convertView;
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private String image_path;
public BitmapWorkerTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(Integer... params) {
try {
while (running) {
Bitmap picture = BitmapFactory.decodeFile(image_path);
int width = picture.getWidth();
int height = picture.getHeight();
float aspectRatio = (float) width / (float) height;
int newWidth = 98;
int newHeight = (int) (98 / aspectRatio);
Log.v("ImageList", "L");
return picture = Bitmap.createScaledBitmap(picture,
newWidth, newHeight, true);
}
} catch (Exception e) {
if (thisLogger != null) {
thisLogger.error(e.toString());
thisLogger.error("\r\n");
}
}
return null;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
Please let me know whats wrong here.

strange behaviour with selecting and showing images from sdcard

I have made a simple android application that displays images in a gridview and you can select some images and send them. This is my getView method.
public View getView(int position, View convertView, ViewGroup parent)
{
View v;
if (convertView == null)
{
LayoutInflater li = getLayoutInflater();
v = li.inflate(R.layout.imageview, null);
final CheckBox cb = (CheckBox) v.findViewById(R.id.checkbox);
cb.setOnCheckedChangeListener(new AddPhotos());
ImageView iv = (ImageView) v.findViewById(R.id.image);
//make image clickable so that it is checked when clicked on the image
iv.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
cb.setChecked((cb.isChecked() ? false : true));
}
});
imagecursor.moveToPosition(position);
uriList.add(imagecursor.getString(image_column_index));
File image = new File(imagecursor.getString(image_column_index));
Bitmap actualBitmap = resizeBitMapImage(image.getPath(), image_width, image_height);
iv.setImageBitmap(actualBitmap);
iv.setLayoutParams(new FrameLayout.LayoutParams(95, 95));
System.gc();
}
else
{
v = convertView;
System.gc();
}
return v;
}
}
Now the problem wit this was that for less images i.e when all images are displayed without any need to scroll, it was working fine. But if there were images not visible and you have to scroll down, then it showed a strange behavior by repeating the earlier images and changing the checked state of the images when you scroll down or up. I changed the getView method like
public View getView(int position, View convertView, ViewGroup parent)
{
View v;
if (convertView == null)
{
LayoutInflater li = getLayoutInflater();
v = li.inflate(R.layout.imageview, null);
}
else
{
v = convertView;
System.gc();
}
final CheckBox cb = (CheckBox) v.findViewById(R.id.checkbox);
cb.setOnCheckedChangeListener(new AddPhotos());
ImageView iv = (ImageView) v.findViewById(R.id.image);
// make image clickable so that it is checked when clicked on the image
iv.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
cb.setChecked((cb.isChecked() ? false : true));
}
});
imagecursor.moveToPosition(position);
uriList.add(imagecursor.getString(image_column_index));
File image = new File(imagecursor.getString(image_column_index));
Bitmap actualBitmap = resizeBitMapImage(image.getPath(), image_width, image_height);
iv.setImageBitmap(actualBitmap);
iv.setLayoutParams(new FrameLayout.LayoutParams(95, 95));
System.gc();
return v;
}
}
Now the order of the images is not changing but scrolling up or down throws an ANR and the checked state is also changed (i guess randomly). the traces.txt shows that it is coming from bitMapImage = BitmapFactory.decodeFile(filePath, options); line in the resizeBitMapImage method which is as below
public static Bitmap resizeBitMapImage(String filePath, int targetWidth, int targetHeight)
{
Bitmap bitMapImage = null;
// First, get the dimensions of the image
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
double sampleSize = 0;
// Only scale if we need to
// (16384 buffer for img processing)
Boolean scaleByHeight = Math.abs(options.outHeight - targetHeight) >= Math.abs(options.outWidth - targetWidth);
if (options.outHeight * options.outWidth * 2 >= 1638)
{
// Load, scaling to smallest power of 2 that'll get it <= desired
// dimensions
sampleSize = scaleByHeight ? options.outHeight / targetHeight : options.outWidth / targetWidth;
sampleSize = (int) Math.pow(2d, Math.floor(Math.log(sampleSize) / Math.log(2d)));
}
// Do the actual decoding
options.inJustDecodeBounds = false;
options.inTempStorage = new byte[128];
while (true)
{
try
{
options.inSampleSize = (int) sampleSize;
bitMapImage = BitmapFactory.decodeFile(filePath, options);
break;
}
catch (Exception ex)
{
try
{
sampleSize = sampleSize * 2;
}
catch (Exception ex1)
{
}
}
}
return bitMapImage;
}
Now i am very new to android development and am very confused. Please any help will be greatly appreciated
EDIT: The images are being added to gridview and the getView method is for the imageAdapter for this gridview.

Categories

Resources