1

As far as I can tell (and correct me if I'm wrong), if you want to have graphical buttons that look good (with nice anti-aliasing) on multiple sized devices, you have to create different a set of different sized images and place the images inside a collection of different directories. I am looking to find a worked example showing exactly which set of directories to use and what the relative sizes need to be for those directories.

By the way, when I say ImageButtons, I mean containing an image instead of text - so 9patch images will not do the trick.

EDIT: One thing that particularly confuses me is the fact that I very often see examples (e.g. chuck258's answer) where people employ different screen densities... but if you make buttons with their width & height tuned for densities, then the fraction of the screen taken up with your button can vary in all sorts of uncontrolled ways, if you have a high-density-but-small-sized screen, then your buttons will be overly large.

EDIT: Further to my last edit, maybe I should be more specific about what I want... let us say that I want a square ImageButton that is approximately 10% (I'll accept a variation from 8% to 13%) of the width of the (portrait only) screen ... what size images would I make and what directories would they be in?

EDIT: I'm getting the message that if you have more screen real estate, then the standard thing to do, is to allow the icons to take up a smaller fraction of the screen. Presumably this is because allowing icons to become very large, makes them look rather toy-like, or childish. BUT the thing is, I'm making game apps for children! So this is exactly what I want.

Mick
  • 8,284
  • 22
  • 81
  • 173
  • 1
    This explains everything: http://developer.android.com/guide/practices/screens_support.html. Most important is to understand the difference between screen density and screen size and how that affects the image resources you need. – Emanuel Moecklin Oct 15 '13 at 13:07
  • 1
    That document is fiendishly complex and the scope for making a mistake is enormous... that's why I'd like to see an example that corresponds to my question. – Mick Oct 18 '13 at 17:29
  • So you put a bounty on your questions but don't mark answers as correct in order to "save" half the bounty... – Emanuel Moecklin Oct 28 '13 at 01:43

8 Answers8

11

First of all you need to understand the difference between screen density and screen size.

The screen size is the physical size, measured as the screen's diagonal. Android groups all actual screen sizes into four generalized sizes: small, normal, large, and extra large. A phone might have a screen size of 4.9 inch which would be considered normal, both Nexus 7 (the old one from 2012 and the new one from 2013) have a screen size of 7" which is large, the Nexus 10 has a screen size of 10" and that's extra large.

The screen density on the other hand is the quantity of pixels within a physical area of the screen; usually referred to as dpi (dots per inch). For example, a "low" density screen has fewer pixels within a given physical area, compared to a "normal" or "high" density screen. For simplicity, Android groups all actual screen densities into four generalized densities: low, medium, high, and extra high (plus the new xxhdpi). An old Nexus 7 has the same screen size as a new Nexus 7 but the old one has a resolution of 1280x800 which is 216 dpi or hdpi while the new one has a resolution of 1920×1200 pixels which is 323 dpi or xhdpi (more pixels within the same physical area means higher pixel density in dpi).

An image in the drawable folder will have the same physical size on small, normal, large and x-large screens if the screens have the same screen density. Because the screens have different sizes the image will take up a different fraction of the screen. On small screens it will take up a larger part in percentage than on a large screen.

Nothing will change if the same image is in one of the screen size folders (drawable-small, drawable-normal, drawable-large, drawable-xlarge) but you can decide to put a larger version of the image in drawable-xlarge. In that case the image would be larger on a Nexus 10 than on a new Nexus 7 (both have xhdpi pixel density).

If the screens have a different pixel density that same image will look differently though. The image would be half the size on an xhdpi screen compared to an mdpi screen (because the xhdpi screen has approximately double the pixel density):
http://developer.android.com/images/screens_support/density-test-bad.png

In case of an icon you usually want it to have the same size on different screens. That's why e.g. menu icons for mdpi screens are 32x32 and those for xhdpi screens are 64x64 and both are in the appropriate drawable folder (drawable-mdpi and drawable-xhdpi):
http://developer.android.com/images/screens_support/density-test-good.png

Now when do you use the pixel density and when do you use the screen size drawable folders?

Pixel density folders are used if the image should have the same physical size on screens with different screen densities which is usually what you want. If you use the same image for an old and a new Nexus 7 it would have a different size even as the screens have the same physical size and that's not what you want. So using density dependent images is imperative.

Screen size folders are used if you want an image to have a different physical size on small, normal, large and x-large screens. If I have a grid navigation with 6 icons on the main screen and I don't want to make use of the extra screen real estate on larger screens (e.g. by adding more icons), then I would provide a small image for the small screen and a large image for the large screen. You would still have to provide density dependent images on top of the screen size dependent images as explained before (example old Nexus 7 vs. new Nexus 7).

So in theory you would need 16 different resources for the same image (4 screen sizes in 4 screen densities or with the new xxhdpi density even 5 densities -> 20 resources). Now of course no one wants to create that many resources especially if you have a lot of images. One approach is to use the Dashboard as someone has suggested: http://developer.android.com/about/dashboards/index.html#Screens
and pick the most commonly used combinations which are small/ldpi, normal/mdpi, normal/hdpi and normal/xhdpi (81% of all devices). That way you bring down the resources to just 4.
Another approach is to provide resources for either screen size or for screen density (again only 4 resources needed) and then do some scaling in code.

If you have screen density dependent resources then you would use e.g. this https://stackoverflow.com/a/5016350/534471 to scale the images down (never up).

If you have screen size dependent resources then you would use this http://developer.android.com/reference/android/util/DisplayMetrics.html to scale the images down.

There's a nice example for all this here (including source code): https://www.captechconsulting.com/blog/steven-byle/understanding-density-independence-android

Now for you specific problem you could use one generic image in the folder drawable. The size of that image should be so that it won't have to be up-scaled (because that would look ugly). You define the button in the layout like this:

<ImageButton
    android:id="@+id/myButton"
    android:src="@drawable/myDrawable"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:scaleType="centerCrop"
    android:adjustViewBounds="true"
/>

Together with this piece of code the button scales to 10% of the screen:

Display display = getWindowManager().getDefaultDisplay();
Point screenSize = new Point();
display.getRealSize(screenSize);
int size = Math.min(screenSize.x, screenSize.y);
int buttonSize = Math.round(size * 0.1f);
ImageButton button = (ImageButton) findViewById(R.id.myButton);
button.setMaxWidth(buttonSize);
button.setMaxHeight(buttonSize);

How large should the original image be?
A Nexus 10 has probably the highest screen resolution of all Android devices at the moment. The 1600 pixels will translate to 3200 density independent pixels on its xhdpi display. 10% of 3200 is 320. If you use a 320x320 image then you will get a good result on all existing devices.
There's a catch to this approach though.
320x320 is pretty large (possibly 24/32 bit color depth) and thus you might run into memory issues. If you provide the same resource in the density dependent drawable folders you can lower the memory footprint for hdpi, mdpi and ldpi devices:

  • drawable-xhdpi: 320x320
  • drawable-hdpi: 240x240
  • drawable-mdpi: 160x160
  • drawable-ldpi: 120x120

The screen size drawable folders could be used to further improve this (smaller screens need smaller images) but then you'll have to provide the 16 or 20 resources as mentioned before. In the end it's a trade-off between memory footprint / speed on one side and maintainability / time to create the resources / apk size on the other side.

Community
  • 1
  • 1
Emanuel Moecklin
  • 28,488
  • 11
  • 69
  • 85
1

I don't know if I get your question right, but for the relative sizes of the Image, see: http://developer.android.com/design/style/devices-displays.html .

You can use a large enough Image for the biggest Screen you want to support. Then use scaleType="fitCenter" in your Image. Some downsides of this:

And yes: ScaleType.FIT_CENTER is the programmatical equivalent the fitCenter property. In general you can expect every Property in your XML has a getter and setter.

chuck258
  • 912
  • 7
  • 16
  • If I want buttons to be a certain (approximate) percentage of the screen width, then this answer does not help. – Mick Oct 18 '13 at 10:31
  • What about creating a large enough Image for the biggest Screen to support and then use fitCenter. I already postet this as a Comment in the Answer below, but you didn't gave a hint if this is the right Way I unserstand your problem – chuck258 Oct 18 '13 at 12:11
  • I gave the comment an upvote... Im experimenting with it programatically. I think it needs a few extra commands like setmaxwidth and setadjustviewbounds and I think the programatic equivalent of fitCenter is setScaleType(ScaleType.FIT_CENTER). So your comment did give me a good set of clues. If you put the complete answer in the main body of your answer I will mark it as correct. – Mick Oct 18 '13 at 12:38
  • Also mentioned some downsides in my answer now – chuck258 Oct 18 '13 at 12:48
  • Well this is embarrassing... my eyesight is not the best, and when I put on my close-up glasses, I noticed that the quality of the anti-aliasing on the resulting images was very poor. The images on my tablet were displayed approximately 72pixels wide. I then took the 500pixel original (that was in my drawables dir) and created 72pix version in the "android asset studio" - there was no contest in terms of quality. – Mick Oct 18 '13 at 13:34
  • Well if you are already in code have a look at this Post: http://stackoverflow.com/questions/17431420/how-to-disable-anti-aliasing-when-scaling-up-an-imageview-in-android Maybe this already helps. Its the opposite of your Problem – chuck258 Oct 18 '13 at 13:39
1

You should at all time create images for all densities. Most images like icons should be fixed height in terms of dp. images of surfaces that aren't fixed height should be 9patch images which are stretchable

More on 9patch here: http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch

Ivo
  • 18,659
  • 2
  • 23
  • 35
  • "icons should be fixed height in terms of dp" - won't that result in huge variations in terms of percentage-of-screen-width? – Mick Oct 15 '13 at 13:32
  • I mean stuff like an refresh icon or menu icon. If you really want a view to be a certain precentage of the screen 9patch images would be the way to go – Ivo Oct 15 '13 at 13:44
  • But surely 9-patch is all about button backgrounds with essentially empty centres? I want a suitably sized icon in the centre of my button. – Mick Oct 15 '13 at 14:04
  • 1
    Then just use the fitCenter property of ImageButton to automatically scale the Image, or whats the Problem with this? You just have to use a reasonable big image resource. – chuck258 Oct 15 '13 at 14:49
  • There's a great cheat-sheet available here: http://petrnohejl.github.io/Android-Cheatsheet-For-Graphic-Designers/ – David Oct 23 '13 at 13:41
1

Usual practice for Android platform is to provide images only for different screen densities, not for screen sizes. If I use tablet I don't want to see big images, I want see more information. So it's better not to increase size of views, but provide different layout for bigger screens.

But if you have some specific reason to provide image 10% of screen width you can use 'drawable' folders with different qualifiers. You are interested in 'Available width', 'Screen size' and 'Screen pixel density' qualifiers. For example provide this images:

  • drawable-sw320dp-mdpi - image 32px width
  • drawable-sw320dp-hdpi - image 48px width
  • drawable-sw320dp-xhdpi - image 64px width
  • drawable-sw400dp-mdpi - image 40px width
  • ... provide images for all devices you want support
Jin35
  • 8,602
  • 3
  • 32
  • 52
  • "provide images for all devices you want support"...but I wish to support about 2000 devices! But luckily I don't need my button to be *exactlty* 10% of the width, just approximately. So presumably there must be some smaller set of sizes and directories that will do the job. – Mick Oct 18 '13 at 13:39
  • It is not necessary provide 2000 images, I mean you can find optimal sizes/densities pairs. See android devices dashboard: http://developer.android.com/about/dashboards/index.html#Screens – Jin35 Oct 18 '13 at 20:07
0

Go to your project-->Ctrl+n-->Android-->Android Icon Set-->Next-->enter name of icon-->next-->image-->browse-->select image from your PC-->None-->finish. thats it it will automatically creates single image in 4 different sizes in 4 folders named

1.drawable-hdpi 2.drawable-mdpi 3.drawable-xhdpi 4.drawable-xxhdpi

so you can use this image in your application it will take that image based the device screen sizes.

Shailendra Madda
  • 20,649
  • 15
  • 100
  • 138
0

You could do something like this:

package com.example.test;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;
import android.view.Menu;
import android.widget.ImageButton;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Get ImageButton from XML
        ImageButton ib = (ImageButton) findViewById(R.id.imageButton);
        //Get the screen size
        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        //10% of the screen width
        int width = (int) (size.x * 0.1);
        //Get and scale the Bitmap (10% of screen width)
        Bitmap bitmap = getImage(R.drawable.ic_launcher, width, width);
        ib.setImageBitmap(bitmap);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }


    public Bitmap getImage (int id, int width, int height) {
        Bitmap bmp = BitmapFactory.decodeResource( getResources(), id );
        Bitmap img = Bitmap.createScaledBitmap( bmp, width, height, true );
        bmp.recycle();
        return img;
    }
}

And the ImageButton:

<ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageButton" />
Dage
  • 226
  • 1
  • 10
  • 1
    Use getRealSize instead of getSize. The doc says "this value should not be used for computing layouts, since a device will typically have screen decoration (such as a status bar) along the edges of the display that reduce the amount of application space available from the size returned here. Layouts should instead use the window size." – Emanuel Moecklin Oct 24 '13 at 14:15
0

This doesn't answer your question exactly. but if you're just looking for generic icons take a look at t IconicDroid library it helped me a lot. You'll be able to resize the icon as much as you want without looking quality and it also saves a lot space so the apk will be smaller. There is a demo of this library on the google Play.

124697
  • 22,097
  • 68
  • 188
  • 315
0

Let's say you nedd imageButotn 10% of width for different Screens sizes it will be 32 for (320x480) mdpi 48 for(480x800|854) hdpi 64 for(640x1024 (galaxy tab 7")) large-hdpi 72 for(720x1280 (nexus 7)) large-tvdipi 72 for(720x1280 (xperia sp,etc) xhdpi 80 for(800x1280 (motorola xoom 10") xlarge-mdpi 108 for(1080x1920 (xperia z,etc) xxhdpi so u make images at all this sizes and put them in the folders they should be.

Eddy
  • 489
  • 4
  • 10
  • That all sounds good except the part "in the folders they should be"... can you tell me the names of these folders. – Mick Oct 25 '13 at 10:08
  • drawable-mdpi, drawable-hdpi,drawable-xhdpi,drawable-xxhdpi,drawable-large-hdpi,drawable-large-tvdpi and drawable-xlarge-mdpi.. Sorry for a late reply... – Eddy Oct 25 '13 at 11:02