Positioning images on multiple screen devices with custom view - android

I have a custom View class which displays a map with few flags located at some positions.
It should look like this :
(Images are taken from Google)
I draw those flags with Canvas's drawBitmap method.
Unfortunately it doesn't display the same exact position in different screen sizes.
I have tried using these :
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inScaled = false;
Bitmap img = BitmapFactory.decodeFile(myFile.getAbsolutePath(), opt);
But it will result exactly same images on all screens (will display a small image in large screen), which is not good.
So the image should be scaled too.
How can I achieve this ?

I've never done this my self but you probably need to account for the density of the screen.
int multiplier = getResources().getDisplayMetrics().density;
I.e this will return 0.75 for LDPI. Then you'll have to use the MDPI as a base line and account for the other types of displays with the multiplier.
E.g
int top = y * multiplier;
int left = x * multiplier;
If I'm totally wrong I'll delete this answer, but this was what came to my mind.

Related

Android converting touch points to actual bitmap points on an image

Here is my problem:
I have an Android application that displays an image. The image itself is resized to 480 x 640 regardless of size.
The user can click on multiple points of the image. Based on where the user clicks on the image, the bitmap itself has some warping applied to it.
So let's say the original image is 1000 x 2000 (using whole numbers to make it simpler).
Once the image is loaded into the ImageView, it scales to display properly in the imageview.
This is obviously different for different phones with different resolutions.
Now when the user clicks on different points, I ultimately want to pass those points to my WCF Service along with the bitmap data to perform some image manipulation.
So the problem for me is how to take the points where the user touched on the phone and convert those to points that are relative to the normal unscaled bitmap.
Summary:
Bitmap is scaled to fit. User Clicks at 100,100. 100,100 is the point relative to the scaled image...not the actual bitmap itself. I'm looking for guidance on how to convert that 100,100 to the point on the actual bitmap.
Thanks in advance for any help you can give.
ok, so the Android ImageView has a default ScaleType of FIT_CENTER, so that means:
public static final Matrix.ScaleToFit CENTER
Compute a scale that will maintain the original src aspect ratio, but
will also ensure that src fits entirely inside dst. At least one axis
(X or Y) will fit exactly. The result is centered inside dst.
so if you're whole image view has 480x640 to show the image, and for example your image is 1000x2000, then:
2000/640 = scaleFactor = 3.125/1
so width will scale down to 320 leaving 80 pixels on either side empty, so it can maintain the aspect ratio.
//this one will be 80.
int xBuffer= (imageViewWidth - (realImageWidth*scaleFactor))/2;
//this one will be zero in your example
int yBuffer = (imageViewHeight - (realImageHeight*scaleFactor))/2;
int imageViewX = 0;//x coord where on the image view it was clicked
int imageViewY = 0;//y coord where on the image view it was clicked
if (imageViewX < xBuffer || imageViewX > imageViewWidth-xBuffer)
{
//ignore the click, outside of your image.
}
else if (imageViewY < yBuffer || imageViewY > imageViewHeight-yBuffer)
{
//ignore the click, outside of your image.
}
else
{
realImageY = imageViewY * scaleFactor;
realImageX = (imageViewY - 80) * scaleFactor;
//save click somehow..
saveClick(realImageX,realImageY);
}

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.

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.

Something going wrong with drawBitmap - with source and dest rectangles

I have a game in which, during one of the levels, sheep can get blown up by mines. The explosion animation is controlled by having a png containing a 4x4 array of explosion images within a 512x515 png file... see below.
I then animate the explosion using the following code:
exp_bitmap_number_to_draw = (int)(time_since_death / 100);
if (exp_bitmap_number_to_draw < 16)
{
explosion_dst_rect.left = b2sx(sheep_x[i]) - sheep_radius * 5;
explosion_dst_rect.right = b2sx(sheep_x[i]) + sheep_radius * 5;
explosion_dst_rect.bottom = b2sy(sheep_y[i]) + sheep_radius * 5;
explosion_dst_rect.top = b2sy(sheep_y[i]) - sheep_radius * 5;
explosion_src_rect.left = 128 * (exp_bitmap_number_to_draw % 4);
explosion_src_rect.top = 128 * (exp_bitmap_number_to_draw / 4);
explosion_src_rect.right = explosion_src_rect.left + 128;
explosion_src_rect.bottom = explosion_src_rect.top + 128;
canvas.drawBitmap(explosion_bitmap, explosion_src_rect, explosion_dst_rect, null);
}
Where explosion_src_rect is a Rect and explosion_dst_rect is a RectF. b2sx() and b2sy() are functions which convert from the absolute coordinates on the sheep on the "playing field" to the coordinates on the screen - its simply adding an offset.
The code works perfectly on several phones I've tried, including a nexus S and a Galaxy S II. But just now a friend tried the code on a samsung galaxy tab 8.9 and found the explosions appeared goofey. He sent me this partial screen grab:
Any idea what could be causing this?
If resources are not available in the correct density, the system loads the default resources and scales them up or down as needed to match the current screen's density.
There are some situations in which you might not want Android to pre-scale a resource. The easiest way to avoid pre-scaling is to put the resource in a resource directory with the nodpi configuration qualifier. For example:
res/drawable-nodpi/icon.png. So you have to put your bitmaps into `res/drawable-nodpi/ folder.
Have a look at: http://developer.android.com/guide/practices/screens_support.html
the width of your destination rectangle, instead of being the size of one of your explosion frames, as you said, is being set to the width of the canvas / 4. Phones are typically of similar size, but the larger tablet has a much larger screen size - As it says in the documentation:
Draw the specified bitmap, scaling/translating automatically to fill the destination rectangle [...]
This function ignores the density associated with the bitmap. This is because the source and destination rectangle coordinate spaces are in their respective densities, so must already have the appropriate scaling factor applied.
So your bitmap, not scaled to the canvas size, is getting drawn starting with the src Rect, but expands beyond the bounds of the src because it's been told to fill the DST, ignoring the densities of the two locations.
As to solution, I'm not sure, exactly - would probably be better to just have images of all sizes raring to go in your /res. Depending on how you load the bitmap, you might be able to get the bitmap to scale, but then your fixed width translation across the bitmap would have to become more dynamic.
Hope that helps somehow :)
I had the same problem which I asked on here and the answer for me was to load my bitmaps like this, rather than the normal way:
getResources().openRawResource(imageId)

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