Android - Google Maps - Projection - toPixels() - Is it device independent pixels? - android

I am using the Map View Projection to obtain the screen pixels like
currentPixelLocation = businessMapMv.getProjection().toPixels(tappedLocation, null);
Then I am using this to do some manipulation on the screen like centering a balloon tip.
So what I do is
currentPixelLocation.y = currentPixelLocation.y - 100
This works fine. Are the pixel locations returned by the toPixels method device independent ?
Will my manioulation like the above code work for all screen resolutions ?

I think there is a misunderstanding in device independent pixels on your site. If you declare your layout in device independent pixels (dip), the framework itself calculates the correct Veiw size - based on device display density - for the device it running on at runtime. After the framework calculates the View dimension, the View has its dimension set in pixels.
So therefore, getProjection().toPixels() gives you the position relative to the underlaying MapView in pixels. Those pixels are device independent.
What seems wrong to me is you calculation currentPixelLocation.y = currentPixelLocation.y - 100 What does the 100 stand for? These 100 are definately device dependent. If you want to subtract 100 pixels, just use this calculation currentPixelLocation.y = currentPixelLocation.y - 100 * getResources().getDisplayMetrics().density + 0.5f. It ensures, that the 100 pixels are calculated device *in*dependent.

They are device independent, but they depend on the tile size that is defined for the tile layer. On regular maps the default tile size is 256 x 256 pixels.

Related

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

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.

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).

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.

Custom rendered Android app widget

I'm making an app widget for Android, which due to being composed of custom elements such as graphs, must be rendered as a bitmap.
However, I've run into a few snags in the process.
1) Is there any way to find the maximum available space for an app widget? (OR: Is it possible to calculate the dimensions correctly for the minimum space available in WVGA (or similar wide) cases?
I don't know how to calculate the maximum available space for an app widget. With a conventional app widget it is possible to fill_parent, and all the space will be used. However, when rendering the widget as a bitmap, and to avoid stretching, the correct dimensions must be calculated. The documentation outlines how to calculate the minimum dimensions, but for cases such as WVGA, there will be unused space in landscape mode - causing the widget to look shorter than other widgets which stretch naturally.
float density = getResources().getDisplayMetrics().density;
int cx = ((int)Math.ceil(appWidgetInfo.minWidth / density) + 2) / 74;
int cy = ((int)Math.ceil(appWidgetInfo.minHeight / density) + 2) / 74;
int portraitWidth = (int)Math.round((80.0f * cx - 2.0f) * density);
int portraitHeight = (int)Math.round((100.0f * cy - 2.0f) * density);
int landscapeWidth = (int)Math.round((106.0f * cx - 2.0f) * density);
int landscapeHeight = (int)Math.round((74.0f * cy - 2.0f) * density);
Calculating cx and cy gives the number of horizontal and vertical cells. Subtracting - 2 from the calculated dpi (e.g. 74 * cy - 2) is to avoid cases where the resulting number of pixels is rounded down. (For example in landscape mode on Nexus One, the height is 110, not 111 (74 * 1.5).
2) When assigning a bitmap to an ImageView which is used as part of the RemoteViews to view the image, there are 2 methods:
2.1) By using setImageViewUri, and saving the bitmap to a PNG file. The image is then served using an openFile() implementation in a ContentProvider:
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
// Code to set up imageFileName
File file = new File(getContext().getCacheDir(), imageFileName);
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
This works, and it's the approach I'm currently using. However, if I set the scaleType of the ImageView to "center", which by the documentation is supposed to "Center the image in the view, but perform no scaling.", the image is incorrectly scaled. Setting the density of the bitmap to DENSITY_NONE or getResources().getDisplayMetrics().densityDpi doesn't make any difference when saving the bitmap to PNG, it seems to be ignored when the file is loaded by the ImageView. The result is that the image is scaled down, due to some dpi issue. This seems to describe the case:
http://code.google.com/p/android/issues/detail?id=6957&can=1&q=widget%20size&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
Because it is not possible to use scaleType:center, the only way I've found to work is to set the layout_width and layout_height of the ImageView statically to a given number of dpis, then rendering the bitmap to the same dpi. This requires the use of scaleType:fitXY. This approach works, but it is a very static setup - and it will not work for resizable 3.1 app widgets (I haven't tested this yet, but unless onUpdate() is called on each resize, this is true).
Is there any way to load an image to an ImageView unscaled, or is this impossible due to a bug in the framework?
2.1) By using setImageViewBitmap directly. Using this method with the Bitmap.DENSITY_NONE setting on the bitmap, the image can be shown without scaling correctly. The problem with this approach is that there is a limitation to how large images can be set through the IPC mechanism:
http://groups.google.com/group/android-developers/browse_thread/thread/e8d84920b999291f/d12eb1d0eaca93ac#01d5c89e5e7b4060
(not allowed more links)http://groups.google.com/group/android-developers/browse_thread/thread/b11550601e6b1dd3#4bef4fa8908f7e6a
I attempting a bit of a hack to get past this issue, by splitting the widget into a matrix of images which could be set in 100x100 pixel blocks. This did allow for larger widgets to work, but ended up being very heavy and failed on large widgets (4x4).
Sorry for a very long post. I've tried to explain a few of the different issues when attempting to use a bitmap rendered app widget. If anyone has attempted the same and have found any more solutions to these issues, or have any helpful comments, this will be highly appreciated.
An approach that worked for us for a similar situation was to generate our graph as a 9-patch png, with the actual graph part as the scalable central rectangle, and the caption text and indication icons (which we did not want all stretched out of shape) and border effects, placed on the outer rectangles of the image.

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.

Categories

Resources