Android: How to override density and just use pixels? - android

I’m using a canvas and bitmap to draw graphics such as circles, rects text etc and want my graphic routines to use actual pixels based on the true screen resolution in pixels just as one would on a PC graphics app. Clearly the Android OS is meant to use densities to try and give the user a good experience independent from the actual device screen size and resolutions. However when I give the command to set pixel x,y then it would be nice to actually have pixel x,y set and not have the system override my coordinates - is it possible to override the system to set pixel x,y?
For instance, if the screen is set for 320x480 (G1 or Hero) it works fine - but on a tablet(IMX515) with screen 800x600 the graphics are always stretched horizontally.
I have tried setting differing densities of Bitmap and Canvas but nothing works - on the tablet they are always stretched to fill the width of the screen.
I have also tried in Manifest
<supports-screens android:anyDensity=”true” />
.
Here are some snippets of code based on a SurfaceView:—
(1) In the Bitmap create and setup (screenWidth, screenHeight are from metrics):-
backBitmap=Bitmap.createBitMap(screenWidth,screenHeight,Bitmap.config.ARGB_8888);
//backbitmap.setDensity(160)
backCanvas=new Canvas(backBitmap)
//backCanvas.setDensity(160);
.
(2) In the drawing routine:-
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
backCanvas.drawCircle(50,50,30,paint);
.
(3) In the Canvas onDraw method:-
protected void onDraw(Canvas canvas){
if (drawingFlag==true){
// canvas.setDensity(160)
canvas.drawBitmap(backBitmap,0,0,null);
}
}
.
.
* Solution ******
Its amazing how sloppy I was - I had been working with Media Player and left the following code in when the SurfaceView was created:-
holder=mySurfaceView.getHolder();
holder.setFixedSize(320,480);
This was forcing the drawing into the 320x480 then scaling across the screen.
Changing as below fixed problem
holder.setFixedSize(800,600)

. . * Solution ** Its amazing how sloppy I was - I had been working with Media Player and left the following code in when the SurfaceView was created:-
holder=mySurfaceView.getHolder();
holder.setFixedSize(320,480);
This was forcing the drawing into the 320x480 then scaling across the screen. Changing as below fixed problem
holder.setFixedSize(800,600)

Related

android drawing to canvas and setting dpi

I created some test assets for ldpi up to xxxhdpi and put them in there respective drawable folders. If I draw things at the corners of the screen say position
0,0 or 0+canvas.getWidth()-bitmap.getWidth()
or things like that they align properly across all device screens.
I'd like to be able to draw my bitmaps anywhere on screen and have them scale adjusted properly across all device sizes but I am just not getting it.
Here's a sample screen shot same code running on two emulators.
The drawing code is the same. I was able to get the Paint to scale the line properly by adding a scale. The bitmaps also scale relative to their screen sizes but how do I draw at 50units in code and have it be 50units on all devices?
init function
Paint p = new Paint();
p.setColor(Color.RED);
p.setStyle(Paint.Style.FILL_AND_STROKE);
float scale = context.getResources().getDisplayMetrics().density;
p.setStrokeWidth(40*scale);
Bitmap ball = BitmapFactory.decodeResource(context.getResources(), R.drawable.ball);
render function
canvas.drawLine(0, 0, 400, 400, p);
canvas.drawBitmap(ball, 50, 50, null);
canvas.drawBitmap(ball2, 150, 150, null);
from the above screenshot you can see two things wrong. The length of the line, it's 400px long. Now that's an just a number I chose but it goes way too far on the smaller phone.
You also see how they are really far apart on one device but literally on top of each other on the second device.
What can to get past this issue?
Looks like your images are not scaled properly in the drawable folders.
According to this post xxhdpi images must be 4 times bigger than ldpi images i.e. 400x400px (xxhdpi) and 100x100px (ldpi).
The line goes from (0,0) all the way to (400, 400). The ldpi screen is only 240x320px, so the bottom right corner coordinate would be (240, 320). What you see is perfectly normal.

Drawing circles on canvas so they are equal or at least comparable sizes on different devices

I have an app that currently needs to draw some circles on a surfaceview's canvas.
Everything works fine, however I'm no understanding how to scale the circles correctly on different devices.
What I mean by this is that on my Galaxy S4, the circles are an appropriate size.
When I load the same app on my wife's Galaxy S3, the circles are huge in comparison.
I'm sure this is due to screen resolution and density, but I'm struggling with how to take the screen resolution/density into account to scale the circles correctly depending on the phone size.
Example of how a circle is drawn in my app. crcl.radius is currently a fixed size of 100. I know this 100 needs to be scaled based on the device but I can't wrap my head around the formula to do so. I'm assuming the conversion is relatively simple.
Disclaimer: somewhat new to Android (about 6 months using it).
myCanvas.drawCircle(crcl.x, crcl.y, crcl.radius, selected_paint);
Thank you in advance for any help you can give.
You can use a dp value instead of pixel. I think this should do the trick.
myCanvas.drawCircle(crcl.x, crcl.y, dipToPixels(getApplicationContext(),crcl.radius), selected_paint);
This function converts dp to pixel.
public static float dipToPixels(Context context, float dipValue) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics);
}

Android bitmap coordinate wierdness

I am trying to develop a custom image display application for android. So far I am able to load a bitmap and display it on the screen. I want to center the users view on the center of the image. To do this, I have been using
Bitmap bmp = BitmapFactory.decodeResource(...)
float offsetX = (androidScreenHeight - bitmapFactoryOptions.outHeight) / 2
canvas.drawBitmap(bmp, offsetX, 0, myPaint)
to render it. androidScreenHeight is correct as far as I can tell. I am using a samsung note which has 1280x800 screen which is the value I am getting for that. My image is 1920 pixels wide, so the offset on each side should be 560, with 800 in the middle for the actual screen. See this picture:
!http://imgur.com/a2DGmjG
The value of offsetX is correct at 560. So I know at least that part is working correctly. But instead of the above, what I am getting is this:
!http://imgur.com/jtHw926
(these are not the actual image)
I am not sure what is going on. Are pixels treated differently somehow on my android device than on my computer? I understand that each pixel will take up a different size on each since the dpi is different. but an offset of 560 pixels should give the same offset on each screen, regardless of the size of the individual pixels. Any ideas what is going on here?
(Promoting my comment to proper answer)
BitmapFactory will perform scaling between that folder's dpi (assuming you have one) and the device dpi. Putting the bitmap in 'drawable-nodpi' will disable the autoscaling but be careful that you really want to do this (cos autoscaling is usually useful and desirable).

Best way to resize and position bitmaps in a canvas so they look the same in different devices?

I am developing a game using a surface panel. I've done a lot of research about how to properly scale and position drawables in the canvas for multiple devices and I came up with a solution that is working fine on phones but has some flaws when I try it on tablets. I am aware that I can use different resources for tablets (and i might end up doing that) but let's assume for now that I don't want to do it, I want to use the same resources for every single different phone in the market.
All the resources that I have are located it in the hdpi folder, and they are properly sized for a 480x800 device.
My approach is similar to the one described here, please take a look on the explanation below, and I would like to know if there is a better solution for this problem!
I have a Galaxy S2 for testing my apps. So my first approach was to manually insert position everything directly in the canvas by trying and finding the best position for everything. Taking the first character position as an example:
draw_x = (float) (19);
draw_y = (float) (279);
canvas.drawBitmap(toDrawBitmap, draw_x, draw_y, null);
When I first tested it in different devices, everything as a mess, out of scale. So digging around I thought about using the density for scaling the resources.
// I am dividing by 1.5 because my initial positions are on a high density device
// so when it goes for a medium density it should scale for 0.66 and a small density
// for 0.5 of my positions.
float scale = getResources().getDisplayMetrics().density /1.5;
draw_x = (float) (19) * scale;
draw_y = (float) (279) * scale;
canvas.drawBitmap(toDrawBitmap, draw_x, draw_y, null);
And at first impression this worked like a charm. It all my characters were in the proper positions. But I noticed that if the device has a different scale widht/height compared to the Galaxy S2 that I am using the problems begin. Although everything was properly positioned part of the image was cut out of the screen, the canvas was calculated larger than the phone screen.
Galaxy S2 is 480x800. My background is also 480x800. When I tested it in the emulator on a small screen resolution 320x480 Android didn't scale my background correctly as I expected it to do so. Instead of scaling it for the right resolution it gave me a background larger than my canvas 320x533.
With some simple math we figure that 320x533 / 480x800 = 0.66. So instead of properly scaling the background in the canvas, it just scaled using the density of the devices.
So my workaround for this problem was the simplest I could think of. I know the resolution of my background, I know the resolution of the phone, so I can calculate the proportion I need and force a resize.
//Set the proportions for scaling in multiple devices
public void setProportions(float screenWidth,float ScreenHeight,Bitmap background){
this.heightProportion = ScreenHeight/background.getHeight();
this.widthProportion = screenWidth/background.getWidth();
}
public Bitmap scaleBitmaps(Bitmap bitmap) {
Bitmap out = Bitmap.createScaledBitmap(bitmap, (int) (bitmap.getWidth() * widthProportion),
(int) (bitmap.getHeight()*heightProportion), false);
return out;
}
That worked fine for the sizes of the drawables, so I just needed to do the same for the positions, using the scale and this new Proportion I was able to calculate using a fixed size background image
public float convertX(float x){
return x* scale * widthProportion;
}
public float convertY(float y){
return y* scale * heightProportion;
}
//calculate the positions applying the scale and the proportion
draw_x = convertX((float) (19));
draw_y = convertY((float) (279));
//draw the bitmap with the scaled position
canvas.drawBitmap(toDrawBitmap, draw_x, draw_y, null);
Long story short, to properly position the drawables I manually set the desired position in my device, calculated a scale between the densities and a porportion between the background image size and the screen size.
To re-size the drawables I just used the proportion because android automatically applies the density scale.
I tested in several different phones and tablets and this approach works perfectly for phones. On tablets it gives me some minor mistakes in the re-sizing of the drawables.
Finally after all this, my question is, what is the problem with this solution? Can I make it work on every phone regardless of the size or there is a better solution for this?
Please note that this strict to Canvas. The same background is re-sized correctly for every phone if I use it in the XML layout. If I wasn't clear or I should give more information please let me know!
The first thing you have to know before solve this problem is about device phone running system. Though you suggest the phone will choose either hdpi or other versions, it depends on each phone running system.
A. Size fitting problem
The problem is how do you process the bitmap. Though you re-scale the bitmap with any math formula, the size of original bitmap will have different output for each different phone. To solve this, you have to set inScaled of bitmap to false.
B. Position fitting problem
Thus you have the problem in fitting the size of bitmap, the position of bitmap will synchronize the position depends on your phone screen size. You should define the background object and positioning the object x and y based on the background. For example if you want to put an object in the middle of phone screen no matter what phone we use it, the code must be `
canvas.drawBitmap(toDrawBitmap, background.getwidth()/2, background.getheight()/2, null);
to solve the fitting position problem.
Let me know what happen.

Devide independence for Canvas class drawing primitives

The Android Canvas class supports a rich set of drawing primitives - circles, lines, etc. I have an app that uses these to chart some statistical data.
After reading the description on http://developer.android.com/reference/android/graphics/Canvas.html#drawLine%28float,%20float,%20float,%20float,%20android.graphics.Paint%29
. . . I was unclear what units the coordinates were in or how to make them device / resolution independent.
What units are these in and what's "best practice" for drawing lines and circles and rectangles that work on lots of different screen sizes and resolutions? Thanks in advance.
Android documentation says "The unit for location and dimensions is the pixel". After experimenting for a while I found out that prior to version 2.0 the unit was the pixel. But starting from 2.0 the unit is most likely the dip (device-independent pixel).
For the following code:
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(0);
canvas.drawRect(new Rect(1, 1, 319, 319), paint);
I get the same size square matching the width of the screen on 320x480, 480x800, and 240x320 emulators with android 2.0+.
This discovery allowed me to solve the problem: 1-pixel vertical lines on a large screen are sometimes 2-pixel wide. Set stroke width to 0 to draw 1-pixel lines independent of the screen size.
Edit
After getting more experience with Android, I need to correct my conclusions. Actually there is an attribute "android:anyDensity" in the "supports-screens" tag of the AndroidManifest.xml. This attribute is true by default. When it's true, the unit of measure is dp, otherwise it's a pixel.

Categories

Resources