Recently, while trying to load a image in imageView I ran into "Out of Heap Memory" error. So I looked on internet and found the way to optimise the images.
I am looking forward to finding the best way to optimise a bitmap.
Currently, I have an ImageView which has 200dp as hight and "match_parent" for width. Basically it fills the screen horizontally and takes 200dp vertically.
I am trying to achieve something like in this image.
Here is my master plan to do so.
1) Create a layout for 1 row (ImageView, TextView, Black Translucent bar behind text)
2) Use RecyclerView and inflate the data
Currently I am stuck on Step 1 (not satisfied with code).
What I did is create an XML layout for the row.
<RelativeLayout
android:id="#+id/layout_adventure_fragment"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:id="#+id/imageView_adventure_home_screen_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:background="#drawable/textview_background_gradient"
android:layout_alignParentBottom="true"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:paddingLeft="15dp"
android:textColor="#FFFFFF"
android:textSize="25sp"
android:id="#+id/textView_adventure_name_home_screen_fragment"
android:layout_width="match_parent"
android:layout_height="55dp"
android:text="Demo Text"/>
</RelativeLayout>
I'll adjust TextView as per Typography guidelines later.
Ok, so basically ImageView is 200dp tall.
To compress my images I used the guide at Android training page which uses inSampleSize and loads image in an AsyncTask.
To calculate the required height I used something I am not sure if it is correct way or not. Since I care to compress image only for height I passed only height to calculate inSampleSize.
DisplayMetrics disp = getApplicationContext().getResources().getDisplayMetrics();
int density = disp.densityDpi;
width = disp.widthPixels;
height = (int)((float)(200.0/160.0) * (float)density);
Bitmap b =decodeSampledBitmapFromResource(getResources(),R.drawable.shake,height);
loadBitmap(R.drawable.shake,iV);
Finally I extracted thumbnail from the final bitmap for the required width and height to show user an image which fills the imageView instead of being just in the centre with margins.
imageView.setImageBitmap(ThumbnailUtils.extractThumbnail(bitmap,width,height));
Full code can be found here : http://pastebin.com/mhKi1sKd
What is the best way to optimise the imageView so that in case multiple images are shown in a RecyclerView, Heap memory does not get full and also if there are unnecessary codes in my program that simply take up processing and valuable time?
This is what I have achieved till now.
Have a look at Picasso library.
Many common pitfalls of image loading on Android are handled
automatically by Picasso:
Handling ImageView recycling and download cancelation in an adapter.
Complex image transformations with minimal memory use.
Automatic memory and disk caching.
Related
I am creating a android app in Xamarin with the mvvmcross framework.
Am trying to display pictures from the server to our users(the pictures are Urls). But the pictures refuse to scale to fill their parent.
How can I achieve the right fit for these images? , I use the FillParent on the Width tag. But the images refuse to scale to the size of the container.
My current setup
<FFImageLoading.Cross.MvxImageLoadingView
android:id="#+id/my_plannymap_listitem_title_picture"
android:layout_width="fill_parent"
android:layout_height="150dp"
local:MvxBind="DataLocationUri ImageUrl; Click OpenDetailCommand" />
above code creates(sorry for the volume thing), As you can see the images do not fill, they remain their "original" size
other activity (same problem here)
<FFImageLoading.Cross.MvxImageLoadingView
android:layout_width="fill_parent"
android:layout_height="150dp"
android:scaleType="fitCenter"
android:id="#+id/overview_image"
android:background="#580AB0"
local:MvxBind="DataLocationUri ImageUrl"
TransparencyEnabled="false"
DownSampleToFit="true" />
It sounds like you want to fix the height to 150dp, and fix the width to the width of the parent. If that is the case it can be done, but it will stretch any image that isn't the correct aspect ratio to begin with.
<FFImageLoading.Cross.MvxImageLoadingView
android:id="#+id/my_plannymap_listitem_title_picture"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="fitXY"
local:MvxBind="DataLocationUri ImageUrl; Click OpenDetailCommand" />
(note I used match_parent since fill_parent is deprecated)
If you don't want your image to stretch in a strange way and you can't guarantee that the aspect ratio of incoming images matches that of your layout, you need to select one aspect (either width or height) to dictate the size of the other, in such a way that the ratio remains unchanged. You can do that with the following (in this case you're deciding the width, and calculating the height from it based on the image's aspect ratio):
<FFImageLoading.Cross.MvxImageLoadingView
android:id="#+id/my_plannymap_listitem_title_picture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="DataLocationUri ImageUrl; Click OpenDetailCommand" />
In almost all cases where you cannot guarantee the aspect ratio of the image you're scaling, the second option gives a better result than the first.
I am going to show some images, but my constraint is as title. I want to make all images have same height but according to their width they must have different width. I just do not want to lose any part of the image when scaling. here is my code:
<com.android.volley.toolbox.NetworkImageView
android:id="#+id/ImageView_IssueFirstLookActivity_magas"
android:layout_width="wrap_content"
android:layout_height="170dp"
android:layout_marginLeft="32dip"
android:layout_weight="2"
android:maxWidth="40dp"
android:adjustViewBounds="true"
android:scaleType="fitStart" />
I tried all I could with different values of android:scaleType but get nowhere.
here is a figure that what I want but when I play with those properties it dose not scale it as I want for example some time it dose not show image width completely and it crops it badly. Another try for example is android:scaleType="fitStart" which cause the width of my images to be fine but the heights are not fit anymore. Any idea?
andriod ImageView attributes does not support the requirement you said just by statically choosing combination of width,height and saleType in the layout xml.
Instead you can do it in the java code by actually finding the width for the image for a given fixed height and setting them programattically to each view in dp(make sure you do the pixel to dp calculation for achieving right output, salable across devices)
If you don't mind an extra dependency, you can do it with Picasso
Picasso.get()
.load(R.drawable.my_image_resource)
.placeholder(R.drawable.ic_image_placeholder)
.error(R.drawable.ic_broken_image)
.resize(0, (int) imagefixedHeight)
// optional:
// .centerInside()
.into(myImageView);
My app has an Activity that displays a vertically scrolling list of ImageButtons. I want each buttons image to (A) come from the assets folder and (B) retain it's aspect ratio as it scales. Unfortunately, my ImageButton doesn't size correctly when it's image comes from the assets folder.
ImageButton src set from drawable
The first screenshot is my test app where the images all come from my apps drawables. That's the "correct" aspect ratio for that image, which is what I want to keep (all of the image buttons have been given scaleType "fitXY" "centerCrop").
ImageButton src set from assets
The second screenshot is my test app where the images all come from my apps "assets" folder — as you can see, the images are stretched out to the full width of the screen as desired, but the original aspect ratio has been lost:
Activity code (MainActivity.java)
LinearLayout buttons = (LinearLayout) findViewById(R.id.buttons);
View button = layoutInflater.inflate(R.layout.snippet_imagebutton, null);
ImageButton imageButton = (ImageButton) button.findViewById(R.id.imageButton);
if (GET_IMAGE_FROM_ASSETS) {
InputStream s = assetManager.open("image.png");
Bitmap bmp = BitmapFactory.decodeStream(s);
imageButton.setImageBitmap(bmp);
} else {
imageButton.setImageResource(R.drawable.image);
}
TextView buttonText = (TextView) button.findViewById(R.id.textView);
buttonText.setText("Button text!");
buttons.addView(button,
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
Button layout (snippet_imagebutton.xml)
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<ImageButton
android:id="#+id/imageButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:gravity="center"
android:textColor="#FFF"
android:background="#88000000"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
Dodgy hack
I've found a dodgy hack which achieves what I want and illustrates the problem — the ImageButton will size correctly to match a scaled up image from the resources, but it won't size correctly to match a scaled up image from the assets. I'd prefer a "real" solution over the dodgy hack if there is one. :)
// to start with, scale the ImageButton based on an image in our
// resources that has the same dimensions as the image in our assets
imageButton.setImageResource(R.drawable.image);
// when we eventually render (we don't have width / height yet)...
imageButton.post(new Runnable() {
public void run() {
int height = imageButton.getHeight();
int width = imageButton.getWidth();
// replace our "resource" image with our "asset" image
imageButton.setImageBitmap(bmp);
// and force the ImageButton to keep the same scale as previously
imageButton.setLayoutParams(
new RelativeLayout.LayoutParams(width, height));
}
});
Summary
I want to get the images for these buttons from my apps "assets" folder. How do I fix my app so that the button images are all scaled properly (i.e. retain their original aspect ratio)?
I'm assuming this has something to do with the framework not actually knowing the width / height of the image before it renders the ImageButton to screen — so how do I fix that? I tried setting adjustViewBounds to "true" on both the RelativeLayout and the ImageButton itself, but that didn't seem to have any effect.
The aspect ratio is not kept when you give the scale type attribute as "fitXY". Try "center", "center_crop" or "center_inside" according to your needs. Check ImageViewScaleType as well.
From what I gather, AdjustViewBounds is actually the method that provides the exact functionality you need. However, as Roman Nurik explains here, AdjustViewBounds does NOT increase the ViewBounds beyond the natural dimensions of the drawable.
Without seeing how you allocated resources/assets, my suspicion would be that this is why you see a difference when loading from Resources and when loading from Assets. Your resource file is - presumably - being scaled up when you load it (since that is the default behavior for resources), so that the actual size of the drawable returned from /res is significantly larger than the bitmap you decode from /assets.
Overriding ImageButton (as Nurik suggests), is of course one option, but probably overkill.
Assuming I am correct about the problem, you should be able to fix it simply by setting your BitmapFactory.options correctly when you load the Assets file. You need to set inDensity and inTargetDensity correctly according to your device (get DisplayMetrics), and set inScaled to true.
Alternatively, you can always rescale the bitmap manually before loading it on the ImageButton. Just grab the screen width to determine how large you need to make it.
P.S.
Auto-scaling is obviously the elegant solution, but it does have a weakness (which applies equally to both resources and assets) - if the width of your display > bitmap witdh when scaled up, you'll likely still have the aspect ratio messed up. You can check this pretty easily by going to landscape mode and seeing whether the resource file still result in the right aspect ratio. If you have a large enough base image (or use different layouts according to the screen size/orientation), then this should not be a problem though. Just something to keep in mind.
I have a normal "read an article" type page with a single large photo at the top.
I'd like it to:
not scale larger than the source imgae
not be larger than the screen
not be CRAZY long vertically
side things I already have working
border (via padding), center crop (would rather top crop, but seems not avail)
The main issue I'm having is - I can either have it fill_parent (ie full width) OR wrap_content (ie as large as the original image). And nothing I have done so far allows the image to be normal size (ie keep from enlarging).
Is there a "norm" for this kind of thing? I'm sure there's some kind of sweet spot or combination of attributes for this that works - I just can't find them.
My current attempt:
<ImageView
android:id="#+id/article_photo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#color/super_light_gray"
android:scaleType="centerCrop"
android:contentDescription="#string/articlePhoto"
android:cropToPadding="true"
android:layout_marginBottom="20dp"
android:padding="1dp"/>
You can write some code in the Activity to get the image size and display size.
If the image size exceeds display size set width to display, if not set to image size. If you want to be fancy you can find the ratio between the x and y and scale vertically also.
New to Android, bad with layouts so this is probably a simple solution but I can't find it. I am adding an image to a ListView, the ListView is defined like this:
<ListView
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"/>
Image is added like this:
ImageView image = new ImageView(context);
image.setScaleType(ImageView.ScaleType.FIT_CENTER);
image.setAdjustViewBounds(true);
image.setImageDrawable(drawable);
return image;
in the ListAdapter. I want the Image to scale so the width fills the screen and the height adjust proportionally. What is happening is the image is scaling down so the height fills the list row but there is plenty of room for the width to be bigger. I had this working earlier but can't figure out what changed. Why is the image shrinking to some seemingly arbitrary height and how do I get it to fill the screen by width?
Edit:
So this happened when I started building for 1.6 instead of 1.5, probably a resolution thing. I guess the image is a bit sharper when it's small but I want it larger, it's almost unusable smaller and I am not able to get a larger image, it's from the web.
I think there is no really "simple" solution, but it's already answered here (without list's, but i don't think it matters):
Android: How to stretch an image to the screen width while maintaining aspect ratio?
Have you tried ImageView.ScaleType.FIT_XY ?
Scale Types