I have an activity containing 4 images in total. They are all matching the resolution of a 1080x1920 device. When I run the activity with those images, which are loaded directly in my activity via the XML, it runs tremendously slow in my Genymotion emulator and lags on a real Android device.
Here is the setup:
<android.support.design.widget.AppBarLayout
...>
<android.support.design.widget.CollapsingToolbarLayout
...>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="vertical"
app:layout_collapseMode="parallax">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageView"
android:src="#drawable/shot_header"
android:scaleType="centerCrop" />
</LinearLayout>
<android.support.v7.widget.Toolbar
.../>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
The first image is in a CollapsingToolbarlayout. The resolution of the image is 1080x649 PNG.
The content_activity:
This image fills the parent width.It's resolution is 1080x772 PNG.
<ImageView
android:layout_width="match_parent"
android:layout_height="250dp"
android:id="#+id/main_image"
android:layout_below="#+id/shot_error_field"
android:src="#drawable/forehand_midpng"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:layout_marginTop="15dp"/>
The other 2 images are in a LinearLayout, their resolution is 500x399
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/main_image">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:id="#+id/imageView3"
android:src="#drawable/forehand_mid_wrong"
android:layout_weight="1"/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" >
</View>
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:id="#+id/imageView4"
android:src="#drawable/forehand_mid_wrong"
android:layout_weight="1"/>
</LinearLayout>
To summarize, I have an activity with 4 ImageViews, populated with properly sized images, which should no problem for a modern Android device. The problem is that this activity is running extremely slow and lagging due to a high memory consumption.
Am I doing something wrong? How can I further optimize those images?
I looked into other threads- out of memory issue but none seems to propose a solution to such a problem.
Problem is resolution of the image, if you can reduce resolution of the image then work fine, here is some example for reducing image resolution and size.
If you pass bitmap width and height then use below function.
public Bitmap getResizedBitmap(Bitmap image, int bitmapWidth,
int bitmapHeight) {
return Bitmap.createScaledBitmap(image, bitmapWidth, bitmapHeight,
true);
}
if you want bitmap ratio same and reduce bitmap size. then pass your maximum size bitmap. you can use this function
public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
int width = image.getWidth();
int height = image.getHeight();
float bitmapRatio = (float)width / (float) height;
if (bitmapRatio > 0) {
width = maxSize;
height = (int) (width / bitmapRatio);
} else {
height = maxSize;
width = (int) (height * bitmapRatio);
}
return Bitmap.createScaledBitmap(image, width, height, true);
}
or if you are using drawable resources then use this method
public Drawable resizeImage(int imageResource) {// R.drawable.large_image
// Get device dimensions
Display display = getWindowManager().getDefaultDisplay();
double deviceWidth = display.getWidth();
BitmapDrawable bd = (BitmapDrawable) this.getResources().getDrawable(
imageResource);
double imageHeight = bd.getBitmap().getHeight();
double imageWidth = bd.getBitmap().getWidth();
double ratio = deviceWidth / imageWidth;
int newImageHeight = (int) (imageHeight * ratio);
Bitmap bMap = BitmapFactory.decodeResource(getResources(), imageResource);
Drawable drawable = new BitmapDrawable(this.getResources(),
getResizedBitmap(bMap, newImageHeight, (int) deviceWidth));
return drawable;
}
/************************ Resize Bitmap *********************************/
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
matrix, false);
return resizedBitmap;
}
Actually this should not be an issue and these images are not so big to make you phone laggy. Take a look on other stuff you have in the application, it is possible there is some heavy operations (like DB writing/readin, API requests) right in UI thread.
If there is no such operations and you see such problems with the perfomance, try to set these images via some libraries, like Picasso.
Gradle dependency:
compile 'com.squareup.picasso:picasso:2.4.0'
And code will look like this:
Picasso.with(this).load(R.drawable.my_image).into(toolbar);
TRY
Definitely reduce the size of the images.
Cache images if you can.
Download the images on a different thread. Store a HashMap would
make it easy for you
When you get the list of image urls, iterate through them:
pictures.put(id,view);
try{
FileInputStream in = openFileInput(id);
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeStream(in, null, null);
view.setImageBitmap(bitmap);
}catch(Exception e){
new Thread(new PictureGetter(this,mHandler,id)).start();
}
Code to update the image view:
if(id!=null){
ImageView iv = pictures.get(id);
if(iv!=null){
try{
FileInputStream in = openFileInput(id);
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeStream(in, null, null);
iv.setImageBitmap(bitmap);
}catch(Exception e){
}
}
Try facebook's fresco library. It can handle large images and one of my projects, reduces memory consumption about 50%.
Related
My goal is to move my finger over the screen to paint on a picture in an imageview mImageView. The picture there is resized to imageview-size to improve performance. Later I want to export this to the big image again, this is where scaleFactor comes in. When multiplying the real device coordinates with this factor, I should get where paint on the real images goes.
path is the uri to the underlying picture.
But it's always off, the areas I want to paint are not being painted in the final image. In general, scaleFactor is too big. Where is the problem? Or is this code right and the problem likely elsewhere?
scaleFactor is defined here for the first time.
private void displayFunction(Uri path) {
iv = (ImageView)findViewById(R.id.mImageView);
int ivWidth = iv.getWidth();
int ivHeight = iv.getHeight();
Bitmap v = null;
try {
v = MediaStore.Images.Media.getBitmap(this.getContentResolver(), path);
}
catch (Exception vc)
{
return;
}
if (v == null) return;
int originalWidth = v.getWidth();
int originalHeight = v.getHeight();
float rs = (float)originalWidth / (float)originalHeight;
Bitmap bitmap2 = null;
if (rs > 1)
bitmap2 = getResizedBitmap(v,(int)((float)ivWidth / rs),ivWidth);
else
bitmap2 = getResizedBitmap(v,ivHeight,(int)((float)ivHeight * rs));
scaleFactor = Math.max((float)originalWidth/(float)ivWidth, (float)originalHeight/(float)ivHeight);
v = null;
bmpTemp = clearPainting(bitmap2, Color.RED, 1, Paint.Style.STROKE);
iv.post(new Runnable() {
public void run() {
iv.setScaleType(ImageView.ScaleType.FIT_XY);
iv.setImageBitmap(bmpTemp);
}
});
}
and
<android.support.design.widget.CoordinatorLayout
android:id="#+id/myCoordinatorLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="de.noahsofie.bildzuschneiden.MainActivity">
<ImageView
android:id="#+id/mImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:contentDescription="#string/html"
android:scaleType="centerCrop" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
You want to display the big image only or paint on it as well? If the goal is to display the large bitmap only, you can draw the paths on the bitmap using canvas, and then scale the bitmap to any size that you want.
I'm using the HorizontalScrollView and I want to put images inside it but images will be larger than the screen and makes users scroll horizontally while I'm using setScaleType(ImageView.ScaleType.CENTER_CROP) or other attributes.
How can I have images completely fit the screen? I prefer to use XML attributes if it is possible.
activity_main.xml
<HorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/main"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
</LinearLayout>
MainActivity.java
ImageView image = new ImageView(this);
image.setImageResource(getResources().getIdentifier('file_name', "drawable", getPackageName()));
image.setAdjustViewBounds(true);
image.setScaleType(ImageView.ScaleType.CENTER_CROP);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
image.setLayoutParams(layoutParams);
parent.addView(image);
You can use this xml property into your ImageView,
android:scaleType="fitXY"
Or use this dynamic code snippet to fit the ImageView accordingly the screen ratio,
private void scaleImage(ImageView view, int boundBoxInDp)
{
// Get the ImageView and its bitmap
Drawable drawing = view.getDrawable();
Bitmap bitmap = ((BitmapDrawable)drawing).getBitmap();
// Get current dimensions
int width = bitmap.getWidth();
int height = bitmap.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.
float xScale = ((float) boundBoxInDp) / width;
float yScale = ((float) boundBoxInDp) / 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 and convert it to a format understood by the ImageView
Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
BitmapDrawable result = new BitmapDrawable(scaledBitmap);
width = scaledBitmap.getWidth();
height = scaledBitmap.getHeight();
// Apply the scaled bitmap
view.setImageDrawable(result);
// Now change ImageView's dimensions to match the scaled image
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
params.width = width;
params.height = height;
view.setLayoutParams(params);
}
private int dpToPx(int dp)
{
float density = getApplicationContext().getResources().getDisplayMetrics().density;
return Math.round((float)dp * density);
}
And sample code for testing:
ImageView image = new ImageView(this);
scaleImage(image , 250); // in dp
Here's the output,
Before,
After,
Source: Scale image into ImageView, then resize ImageView to match the image
I'm trying to scale and crop image at the same time and show it from left to right screen edge. I receive image that is just little bit wider than users screen and I'm able to scale it like this (XML):
<ImageView
android:id="#+id/category_image_top"
android:layout_width="match_parent"
android:layout_height="170dp"
android:maxHeight="170dp"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:focusable="false"
/>
But this is what I get:
I would like to align image to top right like so:
Is this possible? I've tried all scaleTypes but noting works, image is either scaled to fit by X and Y (fitXY, fitStart) or image cropped but centered (centerCrop). I need something like android:scaleType="cropStart"
<ImageView
android:id="#+id/category_image_top"
android:layout_width="match_parent"
android:layout_height="170dp"
android:maxHeight="170dp"
android:scaleType="centerCrop"
android:paddingLeft="half of your screen width"
android:paddingBottom="half of your screen height"
android:adjustViewBounds="true"
android:focusable="false"
/>
You can set padding to move image left or right and also top and bottom padding to move up and down
As I didn't find a way to deal with this situation through xml (views) I turned (as #serenskye suggested) to code. Here's my code, I hope it helps (ps: I've changed my logic a little bit, I wanted to fit image by width so I've scaled it to predefined imageWidght and then cropped it to imageHeight)
//bm is received image (type = Bitmap)
Bitmap scaledImage = null;
float scaleFactor = (float) bm.getWidth() / (float) imageWidth;
//if scale factor is 1 then there is no need to scale (it will stay the same)
if (scaleFactor != 1) {
//calculate new height (with ration preserved) and scale image
int scaleHeight = (int) (bm.getHeight() / scaleFactor);
scaledImage = Bitmap.createScaledBitmap(bm, imageWidth, scaleHeight, false);
}
else {
scaledImage = bm;
}
Bitmap cropedImage = null;
//if cropped height is bigger then image height then there is no need to crop
if (scaledImage.getHeight() > imageHeight)
cropedImage = Bitmap.createBitmap(scaledImage, 0, 0, imageWidth, imageHeight);
else
cropedImage = scaledImage;
iv.setImageBitmap(cropedImage);
add
android:layout_gravity="center"
Android: how to set the height/width of the image(src image intead of the background) of a ImageButton?
I want to set the height/width of the top level image(not the background) image, the size of image should be customized instead of fullfill the button automatically
You can just Use the scaleType attribute of ImageButton.
Set it to fitXY or fitCenter or anything else according to your need.
like this
<ImageButton
android:id="#+id/imageButton"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitCenter"
android:src="#drawable/some_image">
</ImageButton>
You can use Bitmap.createScaledBitmap to scale the image to the size you want.
Bitmap image = ... //load image from your source
image = Bitmap.createScaledBitmap(image, desiredHeight, desiredWidth, true);
xml
<ImageButton
android:id="#+id/picture_id"
android:layout_width="10dp" //here width with unit. 5 dp exemple
android:layout_height="3dp" //here height with unit. 3 dp exemple
android:src="#drawable/picture_name"
/>
EDIT: (java code)
// load the origial BitMap (500 x 500 px)
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
R.drawable.android);
int width = bitmapOrg.width();
int height = bitmapOrg.height();
int newWidth = 200;
int newHeight = 200;
// calculate the scale - in this case = 0.4f
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// rotate the Bitmap
matrix.postRotate(45);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
width, height, matrix, true);
// make a Drawable from Bitmap to allow to set the BitMap
// to the ImageView, ImageButton or what ever
BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);
ImageView imageView = new ImageView(this);
// set the Drawable on the ImageView
imageView.setImageDrawable(bmd);
// center the Image
imageView.setScaleType(ScaleType.CENTER);
// add ImageView to the Layout
linLayout.addView(imageView,
new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT
)
);
I had same problem... change the size of 'src' image of image button(not the size of button just image only).
what i did--
I moved those '.png' file from 'drawable' folder to 'drawable-hdpi' folder.
weird... but it worked.
thanks,
set padding and scaletype to centerInside
Padding will help you customize the source image to provide the required height and width.
<ImageButton
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/location"
android:padding="10dp"
android:scaleType="centerInside"
android:background="#drawable/blue_circle_border"/>
I need to scale down images coming from a Network stream without losing quality.
I am aware of this solution Strange out of memory issue while loading an image to a Bitmap object but it is too coarse - inSampleSize is an integer and does not allow finer control over the resulting dimensions. That is, I need to scale images to specific h/w dimensions (and keeping aspect ratio).
I dont mind having a DIY bicubic/lancoz algorithm in my code but I cant find any examples that would work on Android as they all rely on Java2D (JavaSE).
EDIT:
Ive attached a quick source. The original is 720x402 HD screen capture. Please ignore the top 2 thumbnails. The top large image is resized automatically by android (as part of layout) to about 130x72. It is nice and crisp. The bottom image is resized with API and has severe artifacting
I've also tried using the BitmapFactory and, as I said earlier, it has two problems - no way to scale to exact size and the scaled image is blurry.
Any ideas on how to fix the artifcating?
Thanks, S.O.!
package qp.test;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.widget.ImageView;
public class imgview extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402);
Bitmap resized = getResizedBitmap(original, 130);
//Bitmap resized = getResizedBitmap2(original, 0.3f);
System.err.println(resized.getWidth() + "x" + resized.getHeight());
ImageView image = (ImageView) findViewById(R.id.ImageViewFullManual);
image.setImageBitmap(resized);
}
private Bitmap getResizedBitmap(Bitmap bm, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float aspect = (float)width / height;
float scaleWidth = newWidth;
float scaleHeight = scaleWidth / aspect; // yeah!
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth / width, scaleHeight / height);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
bm.recycle();
return resizedBitmap;
}
private Bitmap getResizedBitmap2(Bitmap bm, float scale) {
/* float aspect = bm.getWidth() / bm.getHeight();
int scaleWidth = (int) (bm.getWidth() * scale);
int scaleHeight = (int) (bm.getHeight() * scale);
*/
// original image is 720x402 and SampleSize=4 produces 180x102, which is
// still too large
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 4;
return BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402, bfo);
}
}
And the layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<!-- <TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="hullo" android:background="#00ff00"
/>
-->
<ImageView android:id="#+id/ImageViewThumbAuto"
android:layout_width="130dip" android:layout_height="72dip"
android:src="#drawable/a000001570402" />
<ImageView android:id="#+id/ImageViewThumbManual"
android:layout_width="130dip" android:layout_height="72dip"
android:src="#drawable/a000001570402"
android:layout_toRightOf="#id/ImageViewThumbAuto"
/>
<ImageView android:id="#+id/ImageViewFullAuto" android:layout_width="300dip"
android:layout_height="169dip"
android:scaleType="fitXY"
android:src="#drawable/a000001570402"
android:layout_below="#id/ImageViewThumbAuto"
/>
<ImageView android:id="#+id/ImageViewFullManual" android:layout_width="300dip"
android:layout_height="169dip"
android:scaleType="fitXY"
android:src="#drawable/a000001570402"
android:layout_below="#id/ImageViewFullAuto"
/>
</RelativeLayout>
You can use BitmapFactory.Options with BitmapFactory.decode function(s),
using inDensity and inTargetDensity
Example: you have 1600x1200 size of image and want to resize to 640x480
then 'inDensity'=5 and 'inTargetDensity'=2 (1600x2 equal to 640x5).
Hoping this help.
The top large image is simply scaled down to 450px by the layout so no artifacts.
The artifacts of the bottom large image result from scaling it down to 130px wide and then up again to about 450px by the layout. So the artifacts are made by your scaling. Try
Bitmap resized = getResizedBitmap(original, 450);
in your code and it should be fine. However, you need to adapt that to the actuall screen width of the phone either.
Briefly, good downscaling algorithm (not nearest neighbor like) consists of 2 steps:
downscale using BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() as close as possible to the resolution that you need but not less than it
get to the exact resolution by downscaling a little bit using Canvas::drawBitmap()
Here is detailed explanation how SonyMobile resolved this task: http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/
Here is the source code of SonyMobile scale utils: http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-android/