Bitmap Image not loading (Blank) - android

I have this issue with loading my Bitmap image. I created a custom ImageAdapter and ImageItem for my GridLayout view. The application is supposed to take a picture, save it to the external storage, and retrieve the image from the external storage, showing it as a small thumbnail in a grid. When that image is clicked from the grid, a new Activity is started, showing the full-sized image. Everything has worked fine so far (The images get saved, and I can actually view them on the file system, the supplied path is correct), but the problem I am facing is that when I load the image, it shows up blank in the GridView, and when it is clicked upon, the new Activity also shows a blank image. I would greatly appreciate any help I can get! Here's the code:
The getView for the custom Adapter:
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
final ImageItem imageItem = (ImageItem) getItem(position);
LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout itemLayout = (LinearLayout) li.inflate(R.layout.image_item, parent, false);
final TextView nameView = (TextView) itemLayout.findViewById(R.id.nameView);
nameView.setText(imageItem.getName());
final ImageView imageView = (ImageView) itemLayout.findViewById(R.id.imageView);
String url = imageItem.getUrl();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
//Bitmap bitmap = BitmapFactory.decodeFile(url);
Drawable d = Drawable.createFromPath(url);
imageView.setImageDrawable(d);
imageView.setLayoutParams(new LinearLayout.LayoutParams(WIDTH, HEIGHT));
imageView.setPadding(PADDING, PADDING, PADDING, PADDING);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
return itemLayout;
} else {
return convertView;
}
}

Related

How to avoid image flickering in a listview

I have a listivew that display a bunch of images. am using Universal Image Loader to load this images from files to imageviews.
This images have different dimensions and i want all of them to have same width but different height in respect to each image aspect ratio.
To achieve this, i have tried setting the following to my imageview
<ImageView
android:layout_width = "400dp"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:adjustViewBounds="true"/>
The issue with this method is that there is a lot of flickering when one scrolls the listview since imageview height is not known in advance and images have to be scaled first using my width to calculate each image height in respect to it's aspect ratio.
How can i calculate each image height in advance instead of letting imageview handle it?
if i have an image which is 400 X 700, and i want the imageview to be 300px wide, how can i calculate imageview's height using my image dimension and maintain image aspect ratio? this can help avoid flickering wnen one scroll the listview.
The reason for this flicker is that, in listview list items are reused. When re-used, the imageviews in the list item retains the old image reference which is displayed first. Later on once new image is downloaded, it starts to show. this causes the flickering behavior.
To avoid this flickering issue, always clear the old image reference from the imageview when it is getting reused.
In your case, add holder.image.setImageBitmap(null); after holder = (ViewHolder) convertView.getTag();
So, your getView() method will look like:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
...
if (convertView == null) {
LayoutInflater inflater = getLayoutInflater();
convertView = inflater.inflate(viewResourceId, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
holder.image.setImageBitmap(null)
}
...
return convertView;
}
After hours of research, i was able to know the method that i can use to calculate new imageview height while maintaining image aspect ratio.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//Returns null, sizes are in the options variable
BitmapFactory.decodeFile("/sdcard/image.png", options);
int width = options.outWidth;
int height = options.outHeight;
//calculating image aspect ratio
float ratio =(float) height/(float) width;
//calculating my image height since i want it to be 360px wide
int newHeight = Math.round(ratio*360);
//setting the new dimentions
imageview.getLayoutParams().width = 360;
imageview.getLayoutParams().height = newHeight;
//i'm using universal image loader to display image
imaheview.post(new Runnable(){
ImageLoader.getInstance().displayImage(imageuri,imageview,displayoptions);
});
You can do something like this :
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//Returns null, sizes are in the options variable
BitmapFactory.decodeFile("/sdcard/image.png", options);
int width = options.outWidth;
int height = options.outHeight;
//If you want, the MIME type will also be decoded (if possible)
String type = options.outMimeType;
How I solved it was by creating a Bitmap[] array variable to store images, then in adapter's getView(), I used position to check if image in Bitmap[] array is null or has value. If it is has value, then I use the value instead of calling the new DownloadImageTask() construct again.
For example:
YourCustomArrayAdapter.java
public class MyCustomArrayAdapter extends ArrayAdapter {
private static Bitmap[] myListViewImageViewsArray = new Bitmap[listViewItemsArray.length];
private String[] myListViewImageURLsArray = new String[listViewItemsArray.length]{
"image_url_1",
"image_url_2",
...
...
};
#Override
public View getView(int position, View view, ViewGroup parent){
CustomViewHolder vHolder;
if(view == null){
view = inflater.inflate(R.layout.movies_coming_soon_content_template, null, true);
vHolder = new CustomViewHolder();
vHolder.imageView = (AppCompatImageView) view.findViewById(R.id.my_cutom_image);
vHolder.imageUrl = "";
view.setTag(vHolder);
}
else{
vHolder = (CustomViewHolder)view.getTag();
// -- Set imageview src to null or some predefined placeholder (this is not really necessary but it might help just to flush any conflicting data hanging around)
vHolder.imageView.setImageResource(null);
}
// ...
// -- THIS IS THE MAIN PART THAT STOPPED THE FLICKERING FOR ME
if(myListViewImageViewsArray[position] != null){
vHolder.imageView.setImageBitmap(myListViewImageViewsArray[position]);
}else{
new DownloadImageTask(position, vHolder.imageView).execute(vHolder.imageUrl);
}
// -- END OF THE FLICKERING CONTROL
}
}
Then, in your image downloader construct, after downloading the image, make an insertion into the Bitmap[] image array for that position. For example:
YourImageDownloaderClass.java
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
AppCompatImageView imageView;
int position;
public DownloadImageTask(int position, AppCompatImageView imageView){
this.imageView = imageView;
this.position = position;
}
#Override
protected Bitmap doInBackground(String...urls) {
String urlOfImage = urls[0];
Bitmap logo = null;
try{
logo = BitmapFactory.decodeStream((InputStream) new URL(urlOfImage).getContent());
}catch(Exception e){
e.printStackTrace();
}
return logo;
}
#Override
protected void onPostExecute(Bitmap result){
if(result != null) {
YourCustomArrayAdapter.myListViewImageViewsArray [position] = result;
imageView.setImageBitmap(result);
}else{
YourCustomArrayAdapter.myListViewImageViewsArray [position] = null;
imageView.setImageResource(null);
}
}
}
my suggestion is to use grid view to avoid flickering of images it will load at first time if it is same url , it will load from cache
Glide.with(mContext)
.load(item.getImageUrl())
.into(holder.mIVGridPic);

addView inside getView only showing the last inserted view

I need to generate some ImageView inside the getView method.
I will generate them inside this layout :
<LinearLayout
android:id="#+id/layoutBaby"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal" >
So the images will be added horizontally automatically (because the orientation).
This is how i add those images :
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
for(int childId : dbHelper.getChildrenIdBySchedule(getItem(position).getId()))
{
ImageView imgBaby = new ImageView(context);
imgBaby.setLayoutParams(params);
String image = dbHelper.getChildImage(childId);
File imgFile = new File(image);
if(imgFile.exists())
{
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
imgBaby.setImageBitmap(myBitmap);
}
else
{
imgBaby.setImageResource(R.drawable.ic_launcher);
}
holder.layoutBaby.addView(imgBaby);
}
I have debug the code above to make sure its called multiple times (based on dbHelper.getChildrenIdBySchedule).
This code working if theres only 1 ImageView, however if there is more than 1 ImageView, only the lastest added ImageView is vissible.
For example, if theres 2 ImageViews, although both of them has been added (i debug the code), only the 2nd ImageView is visible.
Feel free to ask me anything, and Thanks for your time.
You can manage it with HorizontalScrollView :
public View getView(int position, View view, ViewGroup viewgroup) {
ViewHolder holder = new ViewHolder(); // our view holder of the row
if (view == null) {
HorizontalScrollView hr = new HorizontalScrollView(con);
LinearLayout layout = new LinearLayout(con);
layout.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
for(int childId : dbHelper.getChildrenIdBySchedule(getItem(position).getId()))
holder.image = new ImageView(con);
layout.addView(holder.image);
String image = dbHelper.getChildImage(childId);
File imgFile = new File(image);
if(imgFile.exists())
{
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
holder.image.setImageBitmap(myBitmap);
}
else
holder.image.setImageResource(R.drawable.ic_launcher);
}
hr.addView(layout);
view = hr;
view.setTag(holder);
}
holder = (ViewHolder) view.getTag();
return view;
}
For more info, refer this

OutOfMemoryError for Bitmap(List View)

I have read much about out of memory in android due to Bitmap and took various steps to prevent it but it keeps knocking endlessly. Here is my code :
CODE :
in = httpConn.getInputStream();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable=true;
options.inJustDecodeBounds = true;
options.inSampleSize = 1;
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(in, null, options);
I am able to load around 15 images that are with a dimension 640*640. After that it throws me a OutOfMemoryError in the log cat. I have also implemented a external library(from GitHub) for "PullToRefresh" functionality. So obviously that involves a custom list view. Is there any way to avoid this Memory issue.
NOTE : I have already read this. I have also implemented those methods but it still fails to stand up.
Can I use Fragments with List View to avoid this memory issue ? Or, is am I missing something ? Any help would be much appreciated. Thanks in advance.
Just change the options.inSampleSize = 1; to options.inSampleSize = 4;
And also do
bitmap.reCycle();
Well, Now image loading time from internet has many solution. You may also use this library "Android-Query" https://code.google.com/p/android-query/wiki/ImageLoading .It will give you all the required activity.Make sure what you want to do ? and read library wiki page. and solve image loading restriction.
This is my code:-
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
ImageView imageview = (ImageView) v.findViewById(R.id.icon);
AQuery aq = new AQuery(convertView);
String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";
aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback(){
#Override
public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status){
iv.setImageBitmap(bm);
}
});
}
return v;
}

Android, How to add a bitmap layer over another bitmap layer, dynamically from list?

In my application, I have a list adapter. each row includes an imageView in left and two textViews in right. For getting images I'm using Universal-Image-Loader library.
my getView() method is this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = myInflater.inflate(R.layout.list_news_adapter, null);
holder = new ViewHolder();
holder.ivIcon = (ImageView) convertView.findViewById(R.id.list_news_icon);
holder.tvTitle = (TextView) convertView.findViewById(R.id.list_news_title);
holder.tvDate = (TextView) convertView.findViewById(R.id.list_news_date);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvTitle.setText(video.getTitle().get(position));
holder.tvDate.setText(localTime.get(position));
// Load and display image
String imageUrl = (video.getDefaultImage128x96().get(position));
imageUrl = imageUrl.trim().replaceAll(" ", "%20");
imageLoader.displayImage(imageUrl, holder.ivIcon, options);
holder.ivIcon.buildDrawingCache();
Bitmap image = holder.ivIcon.getDrawingCache();
if(image != null) {
image = putOverlay(image, holder.ivIcon.getWidth(), holder.ivIcon.getHeight());
holder.ivIcon.setImageBitmap(image);
holder.ivIcon.destroyDrawingCache();
}
return convertView;
}
in following code, I'm giving list of URLs to that libray to be downloaded in different threads.
// Load and display image
String imageUrl = (video.getDefaultImage128x96().get(position));
imageUrl = imageUrl.trim().replaceAll(" ", "%20");
imageLoader.displayImage(imageUrl, holder.ivIcon, options);
after that in following code, i'm getting current image of imageView (which is downloaded) and ask other method to add a layer on top of this image.
holder.ivIcon.buildDrawingCache();
Bitmap image = holder.ivIcon.getDrawingCache();
if(image != null) {
image = putOverlay(image, holder.ivIcon.getWidth(), holder.ivIcon.getHeight());
holder.ivIcon.setImageBitmap(image);
holder.ivIcon.destroyDrawingCache();
}
putOverlay() method is like this:
public Bitmap putOverlay(Bitmap bmp, int width, int height) {
Bitmap overLay = BitmapFactory.decodeResource(context.getResources(), R.drawable.video_overlay);
Bitmap bmBackground = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
Canvas canvas = new Canvas(bmBackground);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(bmp, 0, 0, paint);
canvas.drawBitmap(overLay, width*3/8, height*3/8, paint);
return bmBackground;
}
It was functional part. What about result!
Universal Image Loader library uses predefined image as long as main image is under downloading. my code can merge this two layers.
However, when main image is downloaded it overrides previous image while i expect my overlay image sits on top of this image.
But, after scroll of screen, i can see my added image (most of images has and some of them doesn't have my added image).
any suggestion/comments would be appreciated.
Solution was easier than the thing that i was thinking.
First I removed those
holder.ivIcon.buildDrawingCache();
Bitmap image = holder.ivIcon.getDrawingCache();
if(image != null) {
image = putOverlay(image, holder.ivIcon.getWidth(), holder.ivIcon.getHeight());
holder.ivIcon.setImageBitmap(image);
holder.ivIcon.destroyDrawingCache();
}
and putOverlay() method from my list adapter and then in XML file of row (list_news_adapter), I added another imageView and set its source attribute with my image.
Wow, it works fine.

Android: showing default image in gallery's ImageView while actual image is downloaded

I believe this is pretty trivial but I can't get it to work.
I want to display a default image in gallery elements (ImageViews) while their actual image is being fetched from the net.
Right now, nothing is shown for an ImageView which its image has yet to arrive. Once it arrives it is immediately shown.
What I tried is right after the instantiation of the ImageView to call its setImageResource function like so:
final ImageView i = new ImageView(mContext);
i.setImageResource(R.drawable.loading);
But it doesn't seem to work. Below is the full getView() function.
Any help is appreciated.
Thanks.
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView i = new ImageView(mContext);
i.setImageResource(R.drawable.loading);
// if the drawbale is in the buffer - fetch it from there
Drawable bufferedImage = DataManager.getInstance().getImagesBuffer()[position];
if (bufferedImage != null){
i.setImageDrawable(bufferedImage);
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
}
// if drawable is not in buffer - fetch it from the net via AsyncImageLoader
else
{
String imageUrl = DataManager.getInstance().getImageBufferInstance().getImageUrl(position);
Drawable downloadedImage = AsyncImageLoader.getInstance().loadDrawable(imageUrl, new ImageCallback() {
public void imageLoaded(Drawable imageDrawable, String imageUrl) {
if (imageDrawable == null)
{
imageDrawable = getResources().getDrawable(R.drawable.icon);
}
i.setImageDrawable(imageDrawable);
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
}
});
i.setImageDrawable(downloadedImage);
}
i.setLayoutParams(new CoverFlow.LayoutParams(Utils.getInstance().getScreenWidth() / 2,
Utils.getInstance().getScreenHeight() / 2));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
return i;
}
Fix your indentation... If I'm reading that correctly, you're only setting the temporary drawable icon in the imageLoaded callback of your (not shown) AsyncImageLoader, which I assume means it's then only being set after the image downloads and is then immediately overwritten with the downloaded image. Try moving the placeholder-setting code into your else block outside the callback.

Categories

Resources