I am using a Nexus 7 1280x800 android 4.2.2 API 17
I want to get the size of the screen to divide it in square sections of the same height and width.
I am using FrameLayout and my squares are subclass of ImageView.
I do this
context.getResources().getDisplayMetrics().heightPixels; ----> 1205
context.getResources().getDisplayMetrics().widthPixels; ------> 800
I suppose 1205 is not 1280 because there are two Android menus on top and bottom of the screen.
Each of my squares is 30x30 px.
To know how many squares I can draw as maximun I do:
int x=1205/30;
When I try to paint my image on coords (x-1)*30 ,y it is drawn partially out of the screen.
How can I know the portion of screen my application can use?
I hope I explained well my issue.
Many thanks.
If all the squares are in the same ImageView, then I would guess that the easiest way is to create your own imageView:
class MyImageView extends ImageView {
Context context;
int myWidth = 0;
int myHeigh = 0;
int numBoxesX = 0;
int numBoxesY = 0;
private final int boxWidth = 30;
private final int boxHeight = 30;
ImageView(Context c) {
super(c);
context = c;
}
}
In the class, you override the onSizeChange function
#Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
myWidth = w;
myHeight = h;
// Set up other things that you work out from the width and height here, such as
numBoxesX = myWidth / boxWidth;
numBoxesY = myHeight / boxHeight;
}
On then create a function to draw the box:
public void drawSubBox(int x, int y, ...) {
// Fail silently if the box being drawn doesn't exist ...
if ((x<0) || (x>=numBoxesX)) return;
if ((y<0) || (y>=numBoxesY)) return;
// Your code to draw the box on the screen ...
}
Once you have created this View, you can add it to a layout, and access the functions for it just like any other view, including any you add to define the size of the subboxes etc etc. So in the class for the Activity with this layout
MyImageView miv;
miv = topView.findViewById("idforMyImageViewSetInLayoutXMLfile");
miv.drawSubBox(0,0, ...);
As to finding out the pixel dimensions of the screen I think you are correct. heightPixels and widthPixels will give you the absolute pixel dimensions of the screen you are using. Just something to be conscious about - I'd be worried about using px as the base measurement since the results will change depending on the density of the screen. It seems like a much safer idea to use dp and after finding the absolute number of pixels in the screen, scalling that number by the dpi so that the rectangles draw the same on every phone. Also the absolute pixel value does not represent the actual free space just the total number of pixles.
Oherwise I don't know that the math works out for this layout to fit on the screen perfectly. x = 1205/30 = 40.166667. Plugging that number in (x-1)*30 = 1,175. Assuming that the x direction on your phone does in fact include 1205px (#JRowan - it could be landscape) adding 30px would seem to make these rectangles fit on the screen. However, this only works if there is no boarder or offset between any of the rectangles. Using the numbers you provided there is only 4.8px of leeway across the 1205px width of the screen. With 40 rectanlges in that space, even a thin boarder would push the drawing off the screen.
I just solved the problem while wondering about margins.
The key is the title bar, which is actualy part of the application screen, but out of the drawing screen.
If I delete the title bar it works perfectly.
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
I spent two days with this and found the answer just now doing the correct question. I leave here links to the posts where I found it.
How to increase the size of the title bar in an Android application?
Height of status bar in Android
Thanks for the answers.
If the grid is in a fragment then the issue is getting the usable screen size before the fragment's view is inflated. In a similar scenario, I got the width and height of the fragment container in the hosting activity and passed these as parameters to the fragment with the grid
Related
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.
I'm working on a game that uses a 2D tile map. The map is rendered via a custom method doDraw() in a SurfaceView. It's a simple double for loop that gets the tile type from a 2D array, determines the corresponding bitmap for that type, then draws the tile to the canvas at the specified coordinates.
Previously I had placeholder .pngs in my drawable folder. These placeholder drawables were simple 100x100 .png files of solid colors that I created in ms paint. Things appeared to be working fine until I swapped out the placeholder drawables with some nice textured drawables. I then realized that the way I was referencing the coordinates to draw the tiles was incorrect and was actually causing them to overlap. This had not been obvious when the tiles were solid colors, but with textures it became pretty obvious that two sides of the tiles were getting covered by adjacent tiles.
I believe the problem is not actually in my doDraw() method but most likely in the way I am referencing the tile sizes. The .png files are 100x100 but they are probably getting converted to a different size based on the pixel density of my device. I think that's what's going on, and I've been reading up how to deal with that, but everything I'm finding out there will generally be talking about a single bitmap, as opposed to many small bitmaps that need to be drawn relative to each other.
Here's the relevant code from my SurfaceView:
public class MapView extends SurfaceView implements SurfaceHolder.Callback {
protected Context context;
public World world;
public Map<Integer,Bitmap> TILE_MAP;
public Bitmap SPRITE;
public Player player;
public Camera camera;
//hardcoded parameters for testing
private int tile_width = 50;
private int tile_height = 50;
public int screen_width = 12; //specifies how many tiles to draw in y direction
public int screen_height = 6; //specifies how many tiles to draw in x direction
public MapThread mapThread;
public MapView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
Log.d("LOGCAT", "MapView created");
//get the tile map
WorldFeatures worldFeatures = new WorldFeatures(context);
TILE_MAP = worldFeatures.TILE_MAP;
SPRITE = worldFeatures.SPRITE;
SurfaceHolder holder = getHolder();
holder.addCallback(this);
}
public void doDraw(Canvas canvas) {
/*the draw method looks a little muddled at the moment as my camera is referencing
the center tile instead of the top left tile. I'll be updating that soon*/
int screenX = 0; //reset screenX each loop
for (int worldX = (camera.x - screen_height/2 + 1); worldX < (camera.x + screen_height/2 + 1); worldX += 1, screenX += 1) {
int screenY = 0; //reset screenY each loop
for (int worldY = (camera.y - screen_width/2 + 1); worldY < (camera.y + screen_width/2 + 1); worldY += 1, screenY += 1) {
canvas.drawBitmap(TILE_MAP.get(world.world_map[worldX][worldY]), screenY*tile_height , screenX*tile_width, null);
if (player.x == worldX && player.y == worldY) { //if the player is standing here, draw the sprite
canvas.drawBitmap(SPRITE, screenY*tile_height + tile_height/5, screenX*tile_width + tile_width/5, null);
}
}
}
}
}
As you can see above, I've hardcoded the tile height and width. A means to abstract this would be nice, but first I'd like to understand what the correct numbers to use are. If I set the height and width for the tiles to 100, I get spaces between the tiles. If set them to 50, I get overlap. This tells me that the correct size to reference is something in between. I could keep guessing until I get it right, but I'd like to understand how to determine what size my drawables scale to for what pixel density.
Currently my drawables are residing in my drawable-hdpi folder, and nowhere else. I'm wondering if I put them in a generic "drawable" folder, will I be able to reference the tile_height and tile_width as 100x100? I've read up on getting intrinsic height and width but that doesn't help when the bitmaps are scaled for the view or device. Even if it's possible to somehow reference the tile sizes based on the original drawable size, I would think there should be a better way than referencing them by any specific number. Shouldn't I be able to find out what the scaled size is while inside my draw method? And then use that to determine what coordinates to draw the tiles to? Even better, is there a way to force what size I want the bitmap to scale to before I draw it to the canvas?
I've read up on pixel density, dpi, drawable folders, etc. at http://developer.android.com/guide/practices/screens_support.html but this doesn't really help me when I'm drawing tiles relative to each other and therefore needing coordinates.
I've also looked into using Rectangles to draw to the canvas since the drawbitmap() method of Canvas that takes Rectangles as an input is not dependent on pixel density. But I'm unsure how practical using Rectangles would be when I have a 12x6 set of tiles to draw. Would I need one Rectangle per tile location?
Any explanations on dealing with scaled bitmaps and/or a better method to draw what I'm drawing would be most helpful.
Answered my own question after getting some sleep. To get the height of a bitmap after android has scaled it automatically, use getWidth() and getHeight() on the Bitmap. For forcing a bitmap to scale to a specific size, use scaleBitmap()
I am programming an app that shows a lot of verses/poems so text wrapping is not an option for me. I would like the text to be as big as possible (doesn't have to recalculate each time a new text is shown, should just allow the biggest text to fit on the screen) without extending screen size. It should not visually scale or take longer for the text to appear.
Is this possible?
Thanks
I would suggest a simple search for the best point size using the largest text that you need to fit. This can be done once at start-up. (Well, maybe twice—once for landscape and once for portrait). The first step would be to initialize a Paint with the typeface you want to use for display. Then call this function to
public void setBestTextSize(String longestText, int targetWidth, Paint paint) {
float size = paint.getTextSize(); // initial size
float w = paint.meaasureText(longestText);
size = targetWidth * size / w;
paint.setTextSize(size);
// test if we overshot
w = paint.measureText(longestText);
while (w > targetWidth) {
--size;
paint.setTextSize(size);
w = paint.measureText(longestText);
}
A binary search in the loop might be theoretically faster, but this should do pretty well since text width does scale approximately linearly with font size and the first step before the loop should get the size pretty close.
An alternative approach, which deals nicely with view size changes, is shown in this thread.
I am writing a View that should show a drawable that seems to "never end".
It should be twice or third the displaysize and move slow through the display.
Therefore I studied some samplecode by Google and found the important Lines
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
canvasWidth = width;
canvasHeight = height;
float sf = backgroundImage.getWidth() / canvasWidth;
backgroundImage = Bitmap.createScaledBitmap(backgroundImage,
(int) (canvasWidth * sf), canvasHeight, true);
}
To rescale the image and than
// decrement the far background
backgroundXPos = backgroundXPos - DELTAMOVE;
// calculate the wrap factor for matching image draw
int newFarX = backgroundImage.getWidth() - (-backgroundXPos);
// if we have scrolled all the way, reset to start
if (newFarX <= 0) {
backgroundXPos = 0;
// only need one draw
canvas.drawBitmap(backgroundImage, backgroundXPos, 0, null);
} else {
// need to draw original and wrap
canvas.drawBitmap(backgroundImage, backgroundXPos, 0, null);
canvas.drawBitmap(backgroundImage, newFarX, 0, null);
}
To draw the moving image. The images is already moving, it's fine.
But, and this is the point of my question, the image looks very ugly. Its original is 960*190 pixels by 240ppi. It should be drawn inside a view with 80dip of height and "fill_parent" width.
It should look same (and good) on all devices. I have tried a lot but I don't know how to make the picture look nice.
Thanks for your help.
Best regards,
Till
Since you're saying that it's a never ending drawable, probably you're writing a game of some sort. If your image is a pixel-art type, then you don't want any scaling; pixel-art-type images cannot be scaled and keep its crisp look (you can try using nearest neighbor interpolation and scaling to an integer multiple of the original, which sometimes might work, but sometimes you will still need manual tweaks). This is the rare case where you actually would need to have different image resource for different screen resolutions.
Otherwise you might want to use a vector image, but if -- as you said -- your original is a high resolution image, then vector image probably won't help much here.
btw, you probably want to show some screenshot. "Looks ugly" is just as helpful as saying my code does not work.
Just a guess, but instead of passing a null paint to your drawBitmap() calls, try making a paint with bitmap filtering disabled:
Paint p = new Paint();
p.setFilterBitmap(false);
canvas.drawBitmap(backgroundImage, backgroundXPos, 0, p);
Hope that helps.
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!