i'm developing a game for android based on the SurfaceView class. Currently i'm stuck trying to optimize the view for different screen-sizes.
Problem 1: i have PNG-Files in my res folder and i draw them using Canvas.drawBitmap. After reading this article ( http://developer.android.com/guide/practices/screens_support.html ) i thought android resizes the bitmaps if the screen is bigger. Unfortunately i recognized no scaling (or any changes) when launching my app on a tablet vs. launching it on a phone. So how is it done? Should i come up with some fancy strategy to calculate the available space and draw my bitmaps using the source- and destination-Rectangle? Would be nice if someone has a best practice or some hints how to draw bitmaps on a canvas. I already read the pixel<->dip conversion topics but i have to say i don't know how to use that "solution" in my app. So any help appreciated...
Problem 2: I have 3 PNGs in my res folder. One ldpi, one mdpi and one hdpi. Ldpi image has 50px*50px, mdpi has 75px*75px and hdpi has 100px*100px. When i load the app in the emulator (800*480) it uses the 100*100 png. When i load the app on the Acer Iconia Tab it uses the 75*75 image. I thought hdpi is for big screens (=tab) and mdpi or ldpi would be used for the emulator screen. At least i thought android would use the hdpi-image also on the tab but i got surprised... Any explantation for this as well much appreciated
Sorry 4 long text, just trying to be clear...
cya
EffDee
For calculating how big your drawing should be, you can use Canvas.getDensity().
The DPI value of a screen depends on the amount of pixels and on the size of the screen. Tablets do have a high resolution, but most are quite large aswell. This leads to a lower DPI value. For more information have a look at http://developer.android.com/guide/practices/screens_support.html
The best practice is to put images into mdpi folder (pixel aspect 1) and then use density with all horizontal dimensions.
For example:
src.left = offsetX * density;
src.top = 0;
src.right = (offsetX + width - 1) * density;
src.bottom = height - 1;
dst.left = x * density;
dst.top = y;
dst.right = (x + width - 1) * density;
dst.bottom = height - 1;
canvas.drawBitmap(myImageStrip, src, dst, null);
For mdpi density is 1.0, ldpi density=0.75 and for hdpi density=1.5
You can obtain these and other values using
density = getResources().getDisplayMetrics().density;
There is another solution: you can put same image in all folders. The system will take proper one, so you dont need to multiply values with density. But in that case, you'll need 3 or more copies of same image.
Related
Although this question is not related to programming, it is related to app development.
Suppose I have an ImageView whose dimensions are 40dp*40dp. If I use the above formula to create an icon for a device with screen density 217 ppi, the size of the icon that fits the ImageView exactly is 55px*55px. If I use this icon in the view in consideration, the result is that the icon does not appear very sharp.
On the other hand, if I use a larger image of size, say 80px*80px, it appears sharp. So this larger image is larger than the exactly fitting one by a factor of 1.6.
What I would like to know is whether there is a certain value of the above factor that is most efficient and conventional. I would like to follow conventions while developing my apps.
Thanks for your time.
Your ImageView size is 40dp, thats equal to:
40 x 1.0 = 40 pixel on mdpi devices
40 x 1.5 = 60 pixel on hdpi devices
40 x 2.0 = 80 pixel on xhdpi devices
40 x 3.0 = 120 pixel on xxhdpi devices
40 x 4.0 = 160 pixel on xxxhdpi devices
Now your device is hdpi (217). for thats device you need a 60x60 pixel bitmap, and you have to put it in the drawable-hdpi directory.
Using drawable directory is like using drawable-mdpi directory. fro each resource in that directory android will scale it up/down before using it.
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 using multi resolution technique number three as written in this article
and to determine the scale factor and stage size, I'm using this piece of code originally written by Jeff :
if (Capabilities.screenDPI >= 200) {
if (Capabilities.screenDPI >= 280) {
AssetFactory.contentScaleFactor = 2;
}
else {
AssetFactory.contentScaleFactor = 1.5;
}
}
else {
AssetFactory.contentScaleFactor = 1;
}
var mViewPort:Rectangle = new Rectangle(0, 0, stage.fullScreenWidth, stage.fullScreenHeight);
mStarling = new Starling(Startup, stage,mViewPort);
mStarling.stage.stageWidth = stage.fullScreenWidth / AssetFactory.contentScaleFactor;
mStarling.stage.stageHeight = stage.fullScreenHeight / AssetFactory.contentScaleFactor;
Then I use this scale factor to determine which sized assets I need to pick.
So now, I have a background which I strech to stage width and size. This technique works great when I'm testing with most devices but then we have devices like the Barnes and Noble Nook Color.
The device has a resolution of 600x1024 with 170 dpi. This means that it's going to pick the smallest assets (320x480) and strech it to 600x1024. Which ofcourse is pixalated. Any ideas on how to get over this issue?
I'm also attaching a test application which shows the issue in detail https://dl.dropbox.com/u/2192209/scaling%20test.zip
What worked for me best so far is not scaling Starling's viewport at all. It should always remain the base size (320x480).
Then you have a scale factor about how big the texture should be. It's a built feature of Starling when creating a texture - passing a scale factor.
What this means is that if your stage is 640x960, your scale factor will be 2. Your image (Bitmap object), will have ACTUAL SCREEN size of 320x480 (fullscreen, 1:1 with stage size), BUT it's texture (loaded asset BitmapData) will be twice as big (640x960, 1:1 with phone size).
For easier understanding - the stage of Starling (320x480) will be scaled up to fit your phone's resolution (320 -> 640). But your images will be scaled down to fit in the stage (640 -> 320). So you get perfectly normal images.
This helps you maintain fixed size of stage. It's really helpful, because otherwise it would be hard to position objects - if you want the object to be in the middle, it's sometimes 160, sometimes 320, etc. This means you always have to set position/size with calculations, which is an overload.
Hope that helps!
edit: Just remembered of a site I've used to determine my main size and assets ratios: http://screensiz.es/phone
How about setting max scale size? If the conclusion of the calculations above takes you to scale that is grater than 2, use bigger assets to reduce the scale ratio.
Your calculation only takes screenDPI in count, try combining them with screen resolution as well.
I have read the "screen support API guide "(http://developer.android.com/guide/practices/screens_support.html) and much more resources, but I cannot understand how the dpi works.
I'm developing a game, and I'm not using any layouts (I'm going to draw all myself with functions like canvas.drawbitmap). But when I use the Canvas.Drawbitmap function I need to specify the pixels of the screen where I want to draw the image.
So I'm working now with a fixed resolution (1280x800) and I'm using the drawable-nodpi folder and adjusting the canvas later if the screen of the phone is wider or narrow. The problem with that is that the images look horrible when the resolution is not the native one (1280x800).
What can I do to solve this problem? I've read and read during 3 days, but all explanations and examples are related to Layouts, Nine Patches and so.
Get the density of the device being used and multiply that density by some base size that you pick (how big or small do you actually want it to be drawn?)
Example:
float objectToDrawHeight = 50; //Specified in plain pixels
float objectToDrawWidth = 50; //Specified in plain pixels
float density = getResources().getDisplayMetrics().density;
objectToDrawHeight *= density;
objectToDrawWidth *= density;
//Draw your object using the new (scaled) height and width
//If you are worried about how the object will look on different aspect ratio devices
// you can get the screen dimensions and use that ratio as a multiplier
// just as you did with density
Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
float screenDimensionX = display.getWidth();
float screenDimensionY = display.getHeight();
Using density and possibly screen dimensions should allow you to draw anything and keep it scaled correctly. When using canvas, assume everything is in pixels, and that you must do the dpi conversion.
I have read all the pages I could find about support multiple screens in android, including
Supporting Multiple Screens
Providing Resources
Screen Sizes and Densities
And many others. But I still don't understand what resources I should provide for it to correctly position drawables(sprites) on a Canvas.
It's my first time making a game and I am currently a game Whack a Mole. I am confused about how to use the ldpi, mdpi, and hdpi, folders and how to properly position sprites to draw over a SurfaceView canvas.
I have a background image, resolution 480x800, so I added it to my hdpi folder. Then I have 120x150 sprites of moles, that I should position correctly on the holes for that background.
Currently I am using the following code to draw it:
canvas.drawBitmap(toDrawBitmap, draw_x, draw_y, null);
draw_x and draw_y are pixels that I found trying to place them correctly:
So far everything is fine, they are correctly placed in my hdpi, 480x800 screen. And android re scales them correctly on the different resolutions.
But when I try to use a different resolution screen, they are all drawn in wrong places, out of the holes, some of them are even out of the screen.
Please correct me if I am wrong but for what I've read these three resolutions are the most common in phones:
240x320 small - ldpi
320x480 normal - mdpi
480x800 normal - hdpi
My goal is to make the game work properly in those three kinds of screen. So my question is:
Can I calculate a draw_x and draw_y value, that will work on all of the devices? If not, how do i solve this problem?
Yes you can calculate it using device width and height.
final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
final int width = display.getWidth();
final int height = display.getHeight();