Android: how to calculate dp? - android

I know there are N threads about this question, but I still don't know how to apply width and height for Widgets (buttons, images) on Android.
I have read this: http://developer.android.com/guide/practices/screens_support.html
and many other threads, but I don't understand it.
How am I supposed to calculate dpi for a button?
Am I need to know the dpi of the device? (Wikipedia list):
http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density
But there are plenty other devices with different dpi's!
I have a design in photoshop (800x480), and a button (290x65). I have saved and imported the image button in the newly created 'drawable' folder in my android project. In XML (layout) I inserted the ImageView with the imported button image.
<ImageView
android:id="#+id/btnNewImage"
android:layout_width="210dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="7dp"
android:src="#drawable/new_image" />
The image inserted is completely different, it's much more bigger. How am I supposed to calculate the exact width and height values for Android sizes?

If you need to know the dpi of a device, use the following snippet:
int dpi = getResources().getDisplayMetrics().densityDpi;
If you want to convert from dp to pixels:
int px = (int)(dp * getResources().getDisplayMetrics().density + 0.5f);
If you want to convert from pixels to dp:
int dp = (int)(px / getResources().getDisplayMetrics().density + 0.5f);

To get your image to show up as the right size, you need to put it in the correct "drawable" folder. For example, if you have an icon that is 48x48 pixels, it should go in drawable-mdpi. Ideally, you should create graphics for each of the folders so that Android can pick the best one based on the screen size and resolution.
I'm using the following guidelines for my icons (which I gleaned from somewhere now forgotten); you can extrapolate on these to figure out what pixel dimensions you need:
Recommended drawable sizes - to match size of launcher icon, about 3/8"
36x36 - low
48x48 - med
72x72 - high
96x96 - extra high

Related

Scaling image resources in Android and Picasso

There are so many material on densities, multiple screen support, so many questions on SO, but I still have not got it.
My goal is simple: display a bitmap as large as available space for ImageView. I need BitMap as I will do some operations on it.
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/figureView" />
I will have some pictures to be used in the bitmap. I will place them in drawables directory. I will reference them with R.drawable.pictureX. I will use Picasso to load them and scale them:
Bitmap bitmap = Picasso.with(getContext()).load(resourceId).resize(w, h).get();
The unclear part for me is all those xxhdpi folders and Android heuristic to select the best.
When a documentation says that Android will automatically scale the image does it apply to my case? I do not want to scale already scaled pictured.
How many dpi variants shall I store in my case and where? Shall I have single file in no-dpi folder or shall I create picture variant for each dpi folder?
How can I determine a dimension for picture resource? It is easy for icons: for example 24x24 dpi and then multiple it with DPI formula. But I want to cover complete screen height. A chapter Configuration examples lists: 240x320 ldpi, 320x480 mdpi, 480x800 hdpi, 720x1280 mdpi, 800x1280 mdpi etc. There are no screen size qualifiers for resources.
Thanks for clarification.
I realized that in fact it is easy to find out Android behavior. I just need each DPI variant different so I can distinguish between them. I put a text with DPI name and pixel resolution inside each picture.
There is a sample GitHub Test DPI project. It has two ImageViews. The first is initialized from XML and Android does scaling. The second is a placeholder that I fill with a BitMap. I entered 200dp as its size and that was correct. Pixel size of MDPI = size in dp.
What I found is logical. Android does not know anything about my intentions. It just selects the best available resource for current DPI.
Nexus 5x uses XXHDPI by default, Samsung SIII Mini uses HDPI. If I delete their default DPI folders, they down-scale higher available variant: XXXHDPI and XHDPI. If I delete all variants except NODPI, this will be used.
And finally if I change the dimensions of ImageView dynamically loaded from source code to 300dp, then it will be scaled and look ugly. Android cannot know at decodeResource execution that I will scale it later and that there is a better resource available.
What remains unknown is how to find out dimensions of picture that fits the screen.
Java:
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inScaled = false;
Bitmap figureBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_test, opt);
ImageView imageView=(ImageView) findViewById(R.id.testView);
imageView.setImageBitmap(figureBitmap);
Activity:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_test" />
<ImageView
android:id="#+id/testView"
android:layout_width="200dp"
android:layout_height="200dp" />
Update 30th august
Original picture: 230x541 px, loading with
Picasso.with(getContext()).load(R.drawable.amalka).into(target)
stored in nodpi, loaded as 230x541 px Bitmap
stored in xxhdpi, loaded as 230x541 px Bitmap
1.Android will automatically scale image in case when you provided image for one screen dencity and there is no such image for another one. e.g. if you add your image only in drawable-xxxhdpi folder, Android will generate images for xxhdpi, xhdpi screen dencities etc.
2.You can save your image in one folder drawable-xxxhdpi and let Android make the downscale, if you don't like the result you can create images for different dencities on your own.
name dencity scale
mdpi 160dpi x1
hdpi 240dpi x1.5
xhdpi 320dpi x2
xxhdpi 480dpi x3
xxxhdpi 640dpi x4
3.You can define dimensions of your images based on size of you ImageView and dpi scale. For example if your ImageView has width 80dp and height 40dp then your image for drawable-mdpi folder should have size 80x40px because mdpi is a baseline dencity, for drawable-hdpi then you need to have image 120x60px (scale is 1.5) etc. For drawable-xxxhdpi your image will be 320x160px.
If you want your image to fit all the screen on all devices you can use parameter android:scaleType="centerCrop", it will make the image take all the space but little part of image can be hidden depending on screen aspect ratio. You can also try to specify image resorces based on shortest dimension of the available screen area, e.g. drawable-sw720dp. Read more about this here.

Where should I put background images in different screensizes?

I've been reading about the drawable-XXdpi folders, and I created my icons in all sizes.
What I want to do now, is to put a background image on my main screen. And what my assumption is at this moment, is that I don't work with DP at this moment but with PX, because my background image needs to fill all the pixels.
When this assumption is correct, where do I put my created background images?
I've created one for 480*320, 800*400 to the largest 1600x2560.
In my assumption I can't put them in the drawable-XXdpi folders because those are DPI related, and I need to fill all the pixels.
You can put your drawable that is not specific for each dpi into drawable-nodpi folder.
But basically, as Der Golem describes, you should always use dp.
I don't work with DP at this moment but with PX ... You should always use DP.
To fill all the available space, just use android:background instead of android:src.
Note that the image will be stretched (if you don't make it a 9 patch - this will also be stretched, but in a "controlled manner").
To create a background with the exact pixel size is not a good practice.
You'd end up wit oversized images.
Better practices include (not mutually exclusive - they can be stacked by using a Layer-List):
stretchables (such as 9 patches)
tiles (seamlessly tileable pictures)
drawables (which are vectorial, like SVG files)
These ones include:
a - shapes
b - gradients
c - ...
SVG (through 3rd party libraries)
...
Also consider that "the exact pixel size" might be different from what you expect.
You have to take in account the StatusBar, the ActionBar/ToolBar, the MenuBar, ...
There is no separate folder architecture for putting px files. You need to use drawable-hdpi , drawable-mdpi, drawable-xhdpi, drawable-xxhdpi.
The approximate pixel values for different dps are like this:
ldpi = 600.00px x 360.00px
mdpi = 800.00px x 480.00px
hdpi = 1200.00px x 720.00px
xhdpi = 1600.00px x 960.00px
xxhdpi = 2400.00px x 1440.00px
Check these links for more details:
http://pixplicity.com/dp-px-converter/
http://developer.android.com/guide/practices/screens_support.html

ImageButtons on different sized devices

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.
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.
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
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
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:
If the Image gets too small you get the same Issues than this Guy mentioned: http://www.pushing-pixels.org/2011/11/04/about-those-vector-icons.html
Of course it is never optimal to load a potential huge Image to scale it down and apply anti alias.
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.
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.
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" />
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.
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.

Android ImageButton relative width

I'd like to ask about one of the hardest thing for me (as a programmer) in android. As I make software mostly without designer (to be honest - totally without a designer) I can't figure out the following:
When I draw a png I use px (not dp) as image width.
I drew a button image with the following sizes:
hdpi: 318x45
mdpi: 212x30
(6 to 4 ratio)
and I would like my button be 80% width of Galaxy s2 which is
Screen Size in Pixels: 480 Pixel x 800 Pixel Screen Size in dp: 352dp
x 587dp
But also I would like to have the same width (80%) for hdpi 10' tablet. Is it possible or I'm missing something?
And is it possible to make 80% but not more than XXX pixel width?
You just need to provide different size images for different screen sizes and densities. When you think about it there's only a few different formats you need to worry about. You will then just create an "80%" size image for each of those formats, place them in the seperate drawable folders, and use them accordingly.
The android docs on supporting multiplce screen size and providing alternative resources are actually pretty comprehensive on how to do this.
Otherwise I'm sorry you don't have a designer. My tip - make your icons originally in Adobe Illustrator if you have it that way you can easily scale them to whatever size you need. As vector images they won't lose quality so you can change the size all you want. When you're ready, just export for web and devices at the size break points and you should be fine.
You have to use the android:layout_weight attribute.
Details outlined in a similar question here

Why are the Width / Height of the drawable in ImageView wrong?

Should be a simple one.
When I pull image.getDrawable().getIntrinsicWidth() I get a value larger than the source image width. It's X coordinate is returning 2880 instead of 1920 which is 1.5 times too big?
I wondered wether the ImageView having a scaleType of "center" effected it but, according to the documentation:
"Center the image in the view, but perform no scaling."
Here is the source:
<ImageView
android:id="#+id/backgroundImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/background"
android:scaleType="center"/>
You said the drawable is from your /res folder. Which folder is it in?
/res/drawable
/res/drawable-mdpi
/res/drawable-hdpi
etc..
And what is the density of the device you are testing on? Is it a Nexus S with general density of 240dpi? Because if your source drawable is located in the drawable-mdpi folder and you are testing on a 240dpi device, then the Android system will automatically scale the drawable up by a factor of 1.5 so that the physical size will be consistent with the baseline device density at 160dpi.
When you call getIntrinsicWidth() what is returned is the size the drawable wants to be after Android scales the drawable. You'll notice that 2880 = 1920 * 1.5
If you placed the drawable in /res/drawable the Android system treats those drawables as meant for mdpi devices, thus the upscaling in your Nexus S. If this was meant for hdpi screens and you do not want this to upscale then try placing it in drawable-hdpi
Have you tried to multiply height and width by density:
getResources().getDisplayMetrics().density
Is your drawable in ressources or download from the web? If it is downloaded, you have to give it the density:
DisplayMetrics metrics = new DisplayMetrics();
getContext().getWindowManager().getDefaultDisplay().getMetrics(metrics);
Resources r = new Resources(getContext().getAssets(), metrics, null);
BitmapDrawable bmd = new BitmapDrawable(r, bitmap);
Check if it is returning the value of it's displayed size, which is not it's actual size. For example, a 50x320px banner ad on a traditional 800x480 phone displays as 75x480.
Should be able to compare against density (or your eyes!) to see what it is doing.
This is probably going to be nothing to with the issue you're having, but just for kicks I'll suggest it to be sure anyway: Are you specifying android:minSdkVersion in your manifest?
Only reason I mention this is because for a while I wasn't doing so in a project, and I learned that this screws the screen density up and caused all sorts of strange problems.

Categories

Resources