Android, get real-world dimensions (inches, cm) from pixel difference? - android

I have two image views that the user can move around on the screen:
<ImageView
android:layout_width="35dp"
android:layout_height="match_parent"
android:id="#+id/leftRulerView"/>
<ImageView
android:layout_width="35dp"
android:layout_height="match_parent"
android:id="#+id/rightRulerView"/>
The are initialized in the following way:
mLeftRuler = (ImageView) view.findViewById(R.id.leftRulerView);
mRightRuler = (ImageView) view.findViewById(R.id.rightRulerView);
When either is moving, I am calculating the actual physical distance between the two views (this will be used for measuring real-world objects). rightRulerView is ALWAYS to the right of the left, and so the distance between them is from the right edge of the left-most view to the left edge of the right view. Refer to the following image:
First, het the pixel separation of the two views:
float distance = mRightRuler.getX() - (mLeftRuler.getX() + mLeftRuler.getWidth());
This works out because the getX() returns the x-position of the left-side of the view. I AM interested in the left side of the right ruler, but I need to add the width of the left ruler to get the location of the right side of it. Then, just subtract the two for the pixel count between the two. This works great and gives the pixel count between the views just fine.
To convert this to inches, I do the following:
DisplayMetrics mDisplayMetrics = getResources().getDisplayMetrics();
float mDPI = (float) mDisplayMetrics.densityDpi;
float measurement = distance / mDPI;
This all works fairly well, but it is not perfect. When is says "1.00 inches" between the two views, it is actually just under 1 1/8 inches (measured with a ruler on the physical screen). The inaccuracy propagates to larger distances (i.e. "3.00 inches" in the app is actually measured to a little over 3 1/4 inches with a ruler on the screen).
There are ruler apps out there, that are accurate to real-world rulers, so how can I convert the pixel distance between the two views into an accurate measurement?

there is to my knowledge at least no way to get a precise physical size without any userinput. what i did , is make a popup where the user has to input the diameter for example and then use some 7th grade math and get to the physical size of the Screen edges, assuming they are squares. and then you have smthing like "0.1mm/pixel" and then you could obviously keep on going with your method.

In DisplayMetrics you can use
mDisplayMetrics.xdpi The exact physical pixels per inch of the screen in the X dimension.
mDisplayMetrics.ydpi The exact physical pixels per inch of the screen in the Y dimension.
Then you can do some simple math to get the real distance

In API 17 getRealMetrics() has been introduced. It should give you accurate values in xdpi and ydpi.
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
However, if you need to support lower API versions there is no way you can achieve your goal without user input and calibration.

Related

Speed and distance not same on every device

I have created an Android game using a canvas, but when testing, I have found that the speed and distance of the movements such as flying up or falling down are set right on a phone with a resolution of 1920x1080 (401 ppi). But when I tested on a smaller phone with a resolution of 480 x 854 (196 ppi), I found that the movement of my sprites are a lot quicker which is affecting the gameplay. E.g. The main character sprite jumps a lot higher than I want it to.
Is there any way of keeping the speed and distance the same across all device sizes and types?
Here is some code on how I have implemented the movement:
A sprite class.
//class variables
private int GRAVITY_LIMIT = -30;
public int gravity = 0;
//gravity
if(gravity>GRAVITY_LIMIT){
gravity= gravity-2;
}
//fall
y= y-gravity;
Drawing the sprite
canvas.drawBitmap(bmp1, x, y, null);
When onTouch is triggered (Jumping)
bird.gravity=30;
You should base your movement around world coordinates. For example, set your world to be 10meters x 10meters, so that when you jump, you jump 1m. You then need to map that world to screen pixels.
float worldHeight = 10f;
float worldToPixels = screenHeight/worldHeight;
y = bird.y * worldToPixels;
So, on a 500px height screen, you would jump 50px and on a 1000px height screen you would jump 100px.
Gravity and other forces need to be based on the world as well for it to work on all devices.
Lastly, if you're trying to make a game for multiple devices, it would be better to use a library like libGDX. There are lots of helpful classes like ViewPorts to make this easier.
I have found another solution.
This one works well.
y= y-(gravity * game.getResources().getDisplayMetrics().density);

strange behaviour of live wallpaper in preview [duplicate]

I am learning how to make live wallpapers, but I have a dilemma I'm sure all who start off have as well.
There is so many resolution screen sizes, how can I just make one set of artwork to be rescaled in code for all versions? I know it's been done as I seen the images in the apk's on a lot of them and they get rescaled.
If it was just one image that did not need any positioning that would be easy, but my problem is I have to get the background image rescaled to fit all devices, I also have animations that fit in a certain x and y position on that background image to fit in place so it looks like the whole background is being animated but only parts of it is (my way of staying away from 300 images of frame by frame live wallpapers).
So the background image needs to be rescaled and the animations need to be rescaled as well to the exact percentage as the background image and they need to sit in a specific x and y position.
Any help would be appreciated so I can get this going.
I tired a few things, figured I would make a scaler for everything example: int scaler; then in onSurfaceChanged scaler = width /1024; //if the bigger image is 1024. that will give me a ratio to work with everywhere. then scale accordingly using scaleBitmap by multiplying the scaler by the image height and width, and also use the same scaler for positioning example image x lets say is at 50, scale it using the same thing x = scaler * 50; that should take care of scaling and positioning, just how to translate all this into java is the next lesson, since I'm new to java, I used to program for flash and php but this is a lot different, take some getting used to. Next thing is how to pan the width, when you move your screen from side to side how to make the image show is the next puzzle I have figure out. Right now it just shows the same width no matter what even though the width is double what the surface shows. If you got an answer or somewhere I can find out the info on this one that would be greatly appreciated.
Well, um, all I can say is "Welcome to the real world." You get your screen dimensions passed to you via onSurfaceChanged, and yes, it is your job to figure out how to scale everything based on this data. That's why they pay us the big bucks. :-)
You will want to make sure your resources are large enough to fit the biggest display you intend to support, so you will always be shrinking things (which distorts much less than expanding things).
Suggest starting with "best practices for screen independence" here: http://developer.android.com/guide/practices/screens_support.html
Additional comments in re your request for more help...
You cannot (necessarily) scale your artwork just using the width, because you need to support multiple aspect ratios. If the screen proportions do not match your artwork, you must decide if you want to distort your artwork, leave blank spaces, etc.
I'm not sure how to interpret your trouble passing around the screen dimensions. Most of us put all of our active code within a single engine class, so our methods can share data via private variables. For example, in the Cube wallpaper in the SDK, onSurfaceChanged() sets mCenterX for later use in drawCube(). I suggest beginning with a similar, simple approach.
Handling scrolling takes some "intelligence" and a careful assessment of the data you receive via onOffsetsChanged(). xStep indicates how many screens your launcher supports. Normally xStep will be 0.25, indicating 5 screens (i.e. xOffset = 0, 0.25, 0.5, 0.75, or 1) but it can be any value from 0 to 1; 0.5 would indicate 3 screens. xPixels gives you an indication of how much the launcher "wants" you to shift your imagery based on the screen you're on; normally you should respect this. On my phone, the launcher "desires" a virtual wallpaper with twice the pixels of the physical screen, so each scroll is supposed to shift things only one quarter of one screen's pixels. All this, and more, is documented in http://developer.android.com/reference/android/app/WallpaperManager.html
This is not "easy" coding--apps are easier than wallpaper. :-)
Good luck...George
P.S. I'll throw in one more thing: somewhere along the line you might want to retrieve the "desired minimum width" of the wallpaper desired by the launcher, so you can explicitly understand the virtualization implicit in xPixels. For example, in my engine constructor, I have
mContext = getApplicationContext();
mWM = WallpaperManager.getInstance(mContext);
mDW = mWM.getDesiredMinimumWidth();
My device has 320 pixel width; I get mDW = 640; as I scroll from screen to screen, xPixels changes by 80 each time...because four scrolls (across five screens) is supposed to double the amount of revealed artwork (this effect is called "parallax scrolling"). The rightmost section has xPixels equals 0; the center (of five) sections has xPixels = -160, etc.
I've used this code snippet to scale one image to fit on different screen sizes.
Bitmap image1, pic1;
image1 = BitmapFactory.decodeResource(getResources(), R.drawable.image1);
float xScale = (float) canvas.getWidth() / image1.getWidth();
float yScale = (float) canvas.getHeight() / image1.getHeight();
float scale = Math.max(xScale, yScale); //selects the larger size to grow the images by
//scale = (float) (scale*1.1); //this allows for ensuring the image covers the whole screen.
scaledWidth = scale * image1.getWidth();
scaledHeight = scale * image1.getHeight();
pic1 = Bitmap.createScaledBitmap(image1, (int)scaledWidth, (int)scaledHeight, true);
Make sure that the edges don't contain vital information as it will be scaled out of the picture on some screen ratios.

android live wallpaper rescaling

I am learning how to make live wallpapers, but I have a dilemma I'm sure all who start off have as well.
There is so many resolution screen sizes, how can I just make one set of artwork to be rescaled in code for all versions? I know it's been done as I seen the images in the apk's on a lot of them and they get rescaled.
If it was just one image that did not need any positioning that would be easy, but my problem is I have to get the background image rescaled to fit all devices, I also have animations that fit in a certain x and y position on that background image to fit in place so it looks like the whole background is being animated but only parts of it is (my way of staying away from 300 images of frame by frame live wallpapers).
So the background image needs to be rescaled and the animations need to be rescaled as well to the exact percentage as the background image and they need to sit in a specific x and y position.
Any help would be appreciated so I can get this going.
I tired a few things, figured I would make a scaler for everything example: int scaler; then in onSurfaceChanged scaler = width /1024; //if the bigger image is 1024. that will give me a ratio to work with everywhere. then scale accordingly using scaleBitmap by multiplying the scaler by the image height and width, and also use the same scaler for positioning example image x lets say is at 50, scale it using the same thing x = scaler * 50; that should take care of scaling and positioning, just how to translate all this into java is the next lesson, since I'm new to java, I used to program for flash and php but this is a lot different, take some getting used to. Next thing is how to pan the width, when you move your screen from side to side how to make the image show is the next puzzle I have figure out. Right now it just shows the same width no matter what even though the width is double what the surface shows. If you got an answer or somewhere I can find out the info on this one that would be greatly appreciated.
Well, um, all I can say is "Welcome to the real world." You get your screen dimensions passed to you via onSurfaceChanged, and yes, it is your job to figure out how to scale everything based on this data. That's why they pay us the big bucks. :-)
You will want to make sure your resources are large enough to fit the biggest display you intend to support, so you will always be shrinking things (which distorts much less than expanding things).
Suggest starting with "best practices for screen independence" here: http://developer.android.com/guide/practices/screens_support.html
Additional comments in re your request for more help...
You cannot (necessarily) scale your artwork just using the width, because you need to support multiple aspect ratios. If the screen proportions do not match your artwork, you must decide if you want to distort your artwork, leave blank spaces, etc.
I'm not sure how to interpret your trouble passing around the screen dimensions. Most of us put all of our active code within a single engine class, so our methods can share data via private variables. For example, in the Cube wallpaper in the SDK, onSurfaceChanged() sets mCenterX for later use in drawCube(). I suggest beginning with a similar, simple approach.
Handling scrolling takes some "intelligence" and a careful assessment of the data you receive via onOffsetsChanged(). xStep indicates how many screens your launcher supports. Normally xStep will be 0.25, indicating 5 screens (i.e. xOffset = 0, 0.25, 0.5, 0.75, or 1) but it can be any value from 0 to 1; 0.5 would indicate 3 screens. xPixels gives you an indication of how much the launcher "wants" you to shift your imagery based on the screen you're on; normally you should respect this. On my phone, the launcher "desires" a virtual wallpaper with twice the pixels of the physical screen, so each scroll is supposed to shift things only one quarter of one screen's pixels. All this, and more, is documented in http://developer.android.com/reference/android/app/WallpaperManager.html
This is not "easy" coding--apps are easier than wallpaper. :-)
Good luck...George
P.S. I'll throw in one more thing: somewhere along the line you might want to retrieve the "desired minimum width" of the wallpaper desired by the launcher, so you can explicitly understand the virtualization implicit in xPixels. For example, in my engine constructor, I have
mContext = getApplicationContext();
mWM = WallpaperManager.getInstance(mContext);
mDW = mWM.getDesiredMinimumWidth();
My device has 320 pixel width; I get mDW = 640; as I scroll from screen to screen, xPixels changes by 80 each time...because four scrolls (across five screens) is supposed to double the amount of revealed artwork (this effect is called "parallax scrolling"). The rightmost section has xPixels equals 0; the center (of five) sections has xPixels = -160, etc.
I've used this code snippet to scale one image to fit on different screen sizes.
Bitmap image1, pic1;
image1 = BitmapFactory.decodeResource(getResources(), R.drawable.image1);
float xScale = (float) canvas.getWidth() / image1.getWidth();
float yScale = (float) canvas.getHeight() / image1.getHeight();
float scale = Math.max(xScale, yScale); //selects the larger size to grow the images by
//scale = (float) (scale*1.1); //this allows for ensuring the image covers the whole screen.
scaledWidth = scale * image1.getWidth();
scaledHeight = scale * image1.getHeight();
pic1 = Bitmap.createScaledBitmap(image1, (int)scaledWidth, (int)scaledHeight, true);
Make sure that the edges don't contain vital information as it will be scaled out of the picture on some screen ratios.

Android - determine specific locations (X,Y coordinates) on a Bitmap on different resolutions?

My app that I am trying to create is a board game. It will have one bitmap as the board and pieces that will move to different locations on the board. The general design of the board is square, has a certain number of columns and rows and has a border for looks. Think of a chess board or scrabble board.
Before using bitmaps, I first created the board and boarder by manually drawing it - drawLine & drawRect. I decided how many pixels in width the border would be based on the screen width and height passed in on "onSizeChanged". The remaining screen I divided by the number of columns or rows I needed.
For examples sake, let's say the screen dimensions are 102 x 102.
I may have chosen to set the border at 1 and set the number of rows & columns at 10. That would leave 100 x 100 left (reduced by two to account for the top & bottom border, as well as left/right border). Then with columns and rows set to 10, that would leave 10 pixels left for both height and width.
No matter what screen size is passed in, I store exactly how many pixels in width the boarder is and the height & width of each square on the board. I know exactly what location on the screen to move the pieces to based on a simple formula and I know exactly what cell a user touched to make a move.
Now how does that work with bitmaps? Meaning, if I create 3 different background bitmaps, once for each density, won't they still be resized to fit each devices screen resolution, because from what I read there were not just 3 screen resolutions, but 5 and now with tablets - even more. If I or Android scales the bitmaps up or down to fit the current devices screen size, how will I know how wide the border is scaled to and the dimensions of each square in order to figure out where to move a piece or calculate where a player touched. So far the examples I have looked at just show how to scale the overall bitmap and get the overall bitmaps width and height. But, I don't see how to tell how many pixels wide or tall each part of the board would be after it was scaled. When I draw each line and rectangle myself based in the screen dimensions from onSizeChanged, I always know these dimensions.
If anyone has any sample code or a URL to point me to that I can a read about this with bitmaps, I would appreciate it.
BTW, here is some sample code (very simplified) on how I know the dimensions of my game board (border and squares) no matter the screen size. Now I just need to know how to do this with the board as a bitmap that gets scaled to any screen size.
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
intScreenWidth = w;
intScreenHeight = h;
// Set Border width - my real code changes this value based on the dimensions of w
// and h that are passed in. In other words bigger screens get a slightly larger
// border.
intOuterBorder = 1;
/** Reserve part of the board for the boardgame and part for player controls & score
My real code forces this to be square, but this is good enough to get the point
across.
**/
floatBoardHeight = intScreenHeight / 4 * 3;
// My real code actually causes floatCellWidth and floatCellHeight to
// be equal (Square).
floatCellWidth = (intScreenWidth - intOuterBorder * 2 ) / intNumColumns;
floatCellHeight = (floatBoardHeight - intOuterBorder * 2) / intNumRows;
super.onSizeChanged(w, h, oldw, oldh);
}
I think I found the answer. I might not be able to find the exact width/height and location of each playable square within a single scaled bitmap, but by looking at the Snake example in the SDK, I see it doesn't create 1 bitmap for the entire board and scale it based on the screen dimensions - instead it creates a bitmap for each tile and then scales the tile based on the screen resolution and the number of tiles wanted on the screen - just like I do when I draw the board manually. With this method, I should be able find the exact pixel boundaries for all of the playable squares on the board. I just have to break the board into multiple bitmaps for each square. I probably will have to do a similar approach for the borders, so I can detect their width/height as well after scaling.
Now I will test it to verify, but I expect it to work based on what I saw in the Snake SDK example.
--Mike
I tested a way to do what I was asking and it seems to work. Here is what I did:
I created a 320 x 320 bitmap for a board. It was made up of a border and squares (like a chess board). The border was 10 pixels in width all the way around the board. The squares were 20 x 20 pixels.
I detected the width and height of the screen through onSizeChanged. On a 480 x 800 display, I would set the new width for the board to be 480 x 480 and use the following code to scale the whole thing:
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
floatBoardWidth = w;
floatBoardHeight = floatBoardWidth;
bitmapScaledBoard = Bitmap.createScaledBitmap(bitmapBoard, (int)floatBoardWidth, (int)floatBoardHeight, true);
super.onSizeChanged(w, h, oldw, oldh);
}
Now in order to detect how many pixels wide the border was scaled to and how many pixels in height & width the squares were scaled to, I first calculated how much the over all image was scaled. I knew the bitmap was 320 x 320, since I created it. I used the following formula to calculate how much the image was scaled:
floatBoardScale = floatScreenWidth / 320;
In the case of a 480 width screen, floatBoardScale equals: 1.5. Then to calculate what my border within the full bitmap was scaled to, I did:
floatBorderWidth = 10 * floatBoardScale;
10 was the original border width in my 320 x 320 bitmap. In my final code I won't hardcode values, I will use variables. Anyway, in the case of this formula, the new calculated border width should be: 15
When I multiplied the same scale factor to the board squares (that were 20 x 20 in the original bitmap) I got new values of 30 x 30. When I used those values in my formulas to calculate what square a person touched, it worked. I touched every corner of the squares and in the center and it always calculated the right location. Which is important, so no matter what the screen resolution, I know where the user wanted to move a piece and visually it shows up in the right location.
I hope this helps anyone who may have had the same question. Also, if anyone has a better method of accomplishing the same thing, please post it.
A couple things. First, start reading about how to support multiple screens. Pay close attention to learning about dips and how they work.
Next, watch this video (at least the first 15-20 minutes of it).
This subject isn't a cakewalk to grasp. I found it best to start playing around inside my code. I would suggest creating a surfaceview and start messing around with some bitmaps, different emulators (screen sizes and densities), and the different types of drawable folders.
Unfortunately, there is more to this topic than I think Google wants to admit, and while it's definitely do-able is isn't simple to get started on it for some types of applications.
Finally, you should consider boiling down your question to be more straight forward if you aren't looking for an abstract answer (like this one).
Good luck!

In opengl, How can I get relation between pixels and gl.gltranslatef(floatx,y,z)?

I am trying to learn opengl stuff on Android. In the gl.gltranslatef(x,y,z) call, I am shifting my texture by some units in the +ve x direction. But I am unable to find the number of pixels does 1 unit of x belong to?
Here is what I am doing:
I call gl.glviewport(0,0,width,height); // This will set my rectangle with 0,0 as lowerleft corner and then extend it to accommodate width and height.
Then
I call to gl.glfrustrum(-5,5,-7,7,3,7); // I am little confused how this call is using the dimensions I set in gl.glviewport.
How will -5 to 5 units from left to right in the above call, translate to pixels on the screen of android?
I mean if width = 320 and height = 533 pixels, then what will be the number of pixels occupied on the screen due to the gl.glfrustrum call?
I am experimenting in the gl.gltranslatef call by specifying xshift as 5.0, but it does not translate the bitmap at the right or left corner of the screen, when I increase it to 6, part of it is still visible on the screen.
Thanks
Siddhesh
In short, I am searching for the maximum number of units (in terms of X) which will represent extreme corners of my android phone screen.
glViewpoint tells it what rectangle (in pixels) your OpenGL output should be displayed in.
glFrustum tells it what coordinates in your "world" units should be mapped to that viewport.
An important point: your glFrustum call includes not only a height and width, but also a depth. Since you are specifying a Frustum, not a cube, that means anything with a Z coordinate anywhere but the very front of your frustum will be scaled down appropriately for its distance from the viewer.
As such, when you to a glTranslatef, the distance by which a particular object will move (in terms of pixels) will depend on its distance from the viewer. The further away it is from the viewer, the fewer pixels a particular sideways or up/down will translate to.
Depending on what else you're doing, one easy way to deal with this might be to use glOrtho instead of glFrustum. glOrtho gives orthographic mode, which means no perspective scaling is done, so a given X or Y distance will translate to the same number of pixels, regardless of distance from the viewer.

Categories

Resources