I need drawable resources for different resolutions, such as 320x480, 800x480, 854x480, 800x600
1024x600, 1024x768, 1024x800. It is a game and the scaling of bitmaps is inacceptable, coz they are very sensitive to it. dpi dependent folders and combination with screen sizes(which are deprecated but no other way to set drawable for large screens with same resolution and differend dpi) are not enaugh. How to distinguish graphics for 1024x600 and 1024x768 for example?
It is so sad to stop use mechanism of auto picking resources and switching to manual loading from assets.
any ideas?
I'm usually using 4 resource folders for drawables:
drawable-mdpi for 320x480
drawable-hdpi for 480x800 and 480x854
drawable-large-hdpi for 600x1024, 768x1024 and so on
drawable-xlarge-mdpi for 800x1280
These are just enough in my mind. Also, you don't need to worry about different drawable resources for, in example, devices with 800x480 and 854x480 screen sizes: you can specify an offset on the edges of your screen equal to 27 pixels and center your game on the screen. Hope this helps.
If you want a pixel perfect fit you could always load the background at runtime via a simple method:
private void setBackgroundImage() {
DisplayMetrics dm = getResources().getDisplayMetrics();
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
// TODO: Conditional Structure to apply the right drawable
// ressource to be applied in the background
}
After you've acquired the resolution of the display you can apply the appropriate background via the setBackgroundDrawable method.
Related
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.
I am using image say 85x85 px (putting this image in drawable-mdpi)
and if i am displaying this image of 85x85 px on [320x480]mdpi screen size device it looks good,
but while displaying this image on [480x800]mdpi device it looks very small.
I wants to know how can i resize this image of (85x85 px) so that it works fine for the device having screen width and height of 480x800, mdpi.
You have to set the image size in the xml in dp. This post is really helpful.
Consider MDPI image(let say 85x85) as baseline Create images as follow
FOLDER ImageSize Percentage
LDPI 64x64 75% of baseline
MDPI 85x85 100% BASELINE
HDPI 127x127 150% of baseline
XHDPI 170x170 200% of baseline
The better solution for this is to produce LDPI,MDPI,HDPI,XHDPI,XXHDPI sized images and place it inside the corresponding folders.
Put the image in /drawable, and define the size of the image in your XML as 85dp, it will then be scaled for LDPI/HDPI/XHDPI/XXHDPI and god forbid XXXHDPI.
However, the better solution would to produce HDPI(128px)/XHDPI(170px)/XXHDPI(255px) version of the graphics and put them in /drawable-hdpi, /drawable-xhdpi, /drawable-xxhdpi respectively. This way you can provide the best experience for your users.
[Edit]
"dp" is Android's way of defining physical size of an UI objects, so a button of 48x48dp will have roughly the same physical dimension even when run on screen sizes between 240x320 to 1080x1920, for more information check Android developer site's Supporting Multiple Screens.
[Edit 2]
For images on canvas you can use this to calculate the scaled size of the image:
DisplayMetrics dm = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(dm);
int newSize = (int)(85 * (dm.densityDpi / 160f));
Is it possible to use common hdpi folder for all screen densities? I have quite a lot images. If I make specific copies to folders drawable-hdpi, drawable-ldpi, drawable-xhdpi, ... but it takes huge data (backgrounds, bitmaps).
Is it possible to set only one drawable folder for all devices and then rescale according to a specific device programmatically?
I think about this code to get display size of the scree:
Display display = getWindowManager().getDefaultDisplay();
width = display.getWidth();
height = display.getHeight();
Then I will get display density of the device, something like this:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
density = metrics.density; // 1 - 1,5 - 2 .....
The I will recalculate size of imageview with density:
ImageView logo = (ImageView)findViewById(R.id.logo);
LinearLayout.LayoutParams logo1 = (LinearLayout.LayoutParams) logo.getLayoutParams();
logo1.width = (int)(logo.getWidth()*density);
logo1.height = (int)(logo.getHeight()*density);
logo1.leftMargin=(int)(logo1.leftMargin*density); // for margin
logo1.topMargin=(int)(logo1.topMargin*density); // for margin
logo1.rightMargin=(int)(logo1.rightMargin*density); // for margin
logo1.bottomMargin=(int)(logo1.bottomMargin*density); // for margin
My main problem is I need to have all proportions of graphic same on all devices. It means I must recalculate imageViews accroding to the screen size.
Is this a right way to get density independent screen? How does android work on other devices if only hdpi folder contains files. Does it take files from this folder? Can I set one common drawable folder to all densities?
I would strongly (strongly) advise against doing this. However, if you want the system to rescale your image assets, design them for mdpi (the baseline density) and put them in drawable/.
That said, you need at least mdpi and hdpi to get reasonable scaling (since hdpi is 1.5x mdpi, scaling algorithms produce worse results than for the other conversions from mdpi).
Make sure you've read and understood Providing Resources and Supporting Multiple Screens before you start dealing with resources.
P.S. The layout solution is wrong for a few reasons (e.g., setting margins instead of size) but it's also the completely wrong thing to do. Don't do it!
I'm drawing a bitmap on a SurfaceView using Canvas.drawBitmap(). I have the bitmap in the res/drawable folder and it seems to scale the bitmap correctly on all devices.
Some users have complained that when they change their device's screen density (using a rooted program called LCDDensity Changer) the bitmaps do not scale accordingly. A large bitmap will get squished as if it is still looking for the old density.
Is there a way to handle density changes when users use programs like LCD Density Changer?
Other info:
1. minSDKVersion=3
2. no targetSDKVersion is set
3. I use getMetrics() to determine screen height and width
4. I'm not using the drawable-hdpi, ldpi, etc folders
One approach,I think you need to use hdpi, ldpi etc., Here is an article on how to do that.Android densities
Second approach, you may get screen density prgormatically using DisplayMetrics class and have different images for each density. Here is SO discussion on get screen density
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
I had multiple problems here. I was targeting v1.5 and I've been using hardcoded constants for screen locations without including a bounding rectangle.
I'm going to:
1. Switch to 2.2 framework (which has better compatability with screen resolutions and densities
2. Use "drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)" rather than "drawBitmap(Bitmap bitmap, float left, float top, Paint paint)". This allows me to specify exactly where I want a graphic drawn and it will scale accordingly.
3. Start using the ldpi, hdpi, xhdpi, etc folders. I'm also considering just using the hdpi folder and allowing other devices to scale the images down. (I wouldn't use xhdpi because I read somewhere that it is only supported in gingerbread 2.3).
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.