简体   繁体   中英

Android image scaling

I'm making an app for 1.6 and I'm having a problem with android image scaling, I have a picture that's 480x295.

On a medium screen this appears correctly, but on a large screen (480x800 or 480x854) it doesn't fill the screen, it android makes the image 1.5x bigger, which is only 720x442.

As 800 is actually 1.67 and 854 is 1.78, I can obviously just include large images for the drawable-hdpi folder, but the image is already 1.5mb, which is larger than people seem to like, and I can't use app2sd as I want to support 1.6.

Any suggestions?

I can only think of three options: 1) Include the larger images (but that limits sales probably, and obviously increases the apk size)

2) Make 2 versions, seems a good solution, just harder to implement.

3) Change to 1.5, and handle all my scaling myself.

EDIT: More details: I'm drawing using canvas and surfaceview Image loading code:

backgroundBMP = BitmapFactory.decodeResource(getResources(), R.drawable.background, null);

And the drawing code:

canvas.drawBitmap(backgroundBMP, 0, 0, null);

If you want to scale an image yourself, put a single copy in:

res/drawable-nodpi

Make sure that resource is in no other folders.

Load the bitmap like this:

Bitmap bitmap = BitmapFactory.decodeResource(

                    context.getResources(),

                    R.drawable.my_image
                );

Resize it like this:

bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);

Then draw it to your canvas:

canvas.drawBitmap(bitmap, 0, 0, null);

That's the least you need to know to get the job done.

Additional notes

Normally, Android chooses your image from one of the res/drawable folders based on the current screen density.

Most image formats don't have density information, so Android makes assumptions about the density of the bitmap based on the folder it was loaded from.

That means it can do it's own scaling to match the density of the loaded bitmap with the canvas density.

Android checks and matches densities once when you load the bitmap, and again when you draw the bitmap.

When you are doing your own scaling you generally want to prevent the OS from interfering. There are several ways you can do it:

1. Use res/drawable-nodpi

This is the strategy I outlined in the quick example above. If you want to package your image as a resource, and still control the scaling yourself, put it here, and only here:

res/drawable-nodpi

If you need to support Cupcake, put a copy in each of these folders instead:

res/drawable-nodpi-v4    
res/drawable

The decoder is automatically provided with options to prevent scaling. Android then sets the image density to to match the screen, so it won't scale when you draw it either. The compiler will apply lossless compression to images placed in res/drawable-xxx folders .

2. Use res/raw

You can leave the image untouched by the compiler if you put it in:

res/raw

But then you need to be careful to prevent scaling at runtime:

BitmapFactory.Options options = new BitmapFactory.Options();    

options.inScaled = false; // Prevents scaling at decode time.

Bitmap bitmap = BitmapFactory.decodeResource(

                    context.getResources(),

                    R.raw.my_image,

                    options
                );

bitmap.setDensity(Bitmap.DENSITY_NONE); // Prevents scaling at draw time.

3. Use the assets folder

If you put your images in the assets folder, instead of in res , you can use a real folder structure to organize them. Load a bitmap asset like this:

Bitmap bitmap = BitmapFactory.decodeStream(

                    context.getAssets().open(

                        "myfolder/my_image.png",

                        AssetManager.ACCESS_BUFFER
                    )
                );

The compiler won't touch it, it loads without scaling, and it's density is set to match the screen, so it won't get scaled when you draw it either.

(PERFORMANCE NOTE: Before Android loads an image, it has to locate the file. A resource is faster to locate than an asset , so there may be some performance penalty if you package your images as assets.)

If you wanna scale your Bitmap you can do that by using canvas.scale(scaleX, scaleY); before canvas.drawBitmap() . You could calculate the aspect ratio and use that as your scale value.

One important thing to add is that you can use the "Move to SD card" feature using <uses-sdk android:minSdkVersion="3" /> and still support lower Android versions, specified by minSdkVersion .

Like suggested by Viktor, you can do a Canvas.scale(). You could also draw the bitmap using a BitmapDrawable (set as the View's background) and the scale will be done for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM