Strange thing happens - at least I don't get it.
I have an image (w: 120px, h: 63px) which is presented on one ImageButton. The only variant of that image that I have is placed in drawable-hdpi (and there is no other drawable directory).
Everything is ok with this image and every resolution (density), Android takes care of it very nicely.
But, trouble is when I take a photo from an album (some very big photo, for example), scale it to dimensions of a button (previously mentioned, w: 120px h: 63px) and set that small image as ImageButton background.
In case when I am testing on emulator with medium density (160) it is ok, but when testing on device with high density button gets resized since scaled image appears smaller.
Does anyone has an idea what is happening and why is size of image changed once more after I scaled it?
Any clue will be highly appreciated.
Here is the code I use for resizing the image:
public static void resizeAndSaveImage(String pathToResize, int newWidth, int newHeight, FileOutputStream output) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(pathToResize), null, options);
if (bitmap != null) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, output);
output.close();
}
} catch (Exception e) {
// ...
}
Dimension parameters are passed in as follows: newWidth = 120, newHeight = 63.
In order to achieve what you want you need to use density-independent pixels (dp) and not physical pixels. Reading from here:
The density-independent pixel is
equivalent to one physical pixel on a
160 dpi screen, the baseline density
assumed by the platform (as described
later in this document). At run time,
the platform transparently handles any
scaling of the dp units needed, based
on the actual density of the screen in
use. The conversion of dp units to
screen pixels is simple: pixels = dps
* (density / 160). For example, on 240 dpi screen, 1 dp would equal 1.5
physical pixels. Using dp units to
define your application's UI is highly
recommended, as a way of ensuring
proper display of your UI on different
screens.
In order to explain in more detail why this is happening with one image (album photo) and not with an image resource, you need to post the code you are using for the scaling.
Related
I am making a new app, and I am using a Canvas. I am scaling the canvas but I set the size to:
public static final int WIDTH = 1920;
public static final int HEIGHT = 1080;
(the screen will be in landscape. That is why width is bigger than height)
Meaning if this app was intended for portrait orientation:
public static final int WIDTH = 1080;
public static final int HEIGHT = 1920;
Scaled as the canvas usually is:
public void render(Canvas c) {
super.draw(c);
final float scaleFactorX = getWidth()/(WIDTH*1.f);
final float scaleFactorY = getHeight()/(HEIGHT*1.f);
if (c != null) {
final int savedState = c.save();
c.scale(scaleFactorX, scaleFactorY);
/////render
/////render end
c.restoreToCount(savedState);
}
}
Most phones today are HD or better, and very few have worse resolution. But there are still phones, and I am concerned as to how the scaling will react on other non-HD/better phones as my app will be pushing for a better resolution than the screen supports.
Any ideas how the phone will respond to this? Some phones can auto-adjust the scaling but is that a general function or a function only some phones have?
The SurfaceView is activated in an Activity and set using setContentView:
#Override
public void onCreate(Bundle sis){
super.onCreate(sis);
CustomSurfaceView sf = new CustomSurfaceView(this);
setContentView(sf);
//initialize
sf.init(this, this);
}
EDIT
To clarify:
I am scaling the canvas to a specific size that is the same as HD 1080 resolution. HD 1080 screens will not do any scaling compared to the screen. 2K screens will scale to HD 1080 resolution, meaning it will go with a lower resolution than the screen's max fit.
HD1080 size is 1080x1920 which will be applied to a canvas on a screen that is smaller than that size.
But how will the scaling act on HD 720 screens or in general worse resolution than HD 1080? The app will be pushing a bigger size than the screen supports. How will the phone react to this?
Running the app on a nexus emulator(nexus 4, api 23) results to the canvas being pushed slightly off the screen. Not all phones behave like nexus as the firmware has been edited by the manufacturers, so just because it goes off the screen on the Nexus doesn't mean it will on a Sony or any other brand
Note: HD references to HD 1080 unless otherwise defined.
Don't hard code resolution. It's not recommended. Android has build-in mechanism for scaling graphics by using DP unit while drawing. detail on unit
Convert DP into pixels by dps * getResources().getDisplayMetrics().density detail
Also walk through official docs
Supporting Multiple Screens
Screen Sizes and Densities
By the way drawing on fixed HD-1080 canvas and then resize it for other resolution will work as follow.
Let your CustomSurfaceView take full screen of device. (Don't hard code size of your view like canvas)
Use same scaleFactor for xScale and yScale to preserve ratio. Keep in mind that you will get black screen on sides in devices having other than 16:9 aspect ratio. (You can reposition your canvas to center and draw some kind of frame on sides)
Use following code to calculate ratio. (Help is taken from here and here)
public void render(Canvas c) {
super.draw(c);
float scaleFactor = 1f;
if ((getWidth()> 0) || (getHeight() > 0)) {
scaleFactor = Math.min(WIDTH/getWidth()*1f, HEIGHT/getHeight()*1f);
}
if (c != null) {
c.scale(scaleFactor, scaleFactor );
//your code
}
}
I am loading a bitmap that is stored in the drawable-hdpi folder (with all the other images).
options.inScaled = false;
Bitmap rawBMP = BitmapFactory.decodeResource(res, resId, options);
Bitmap finalBMP = Bitmap.createScaledBitmap(rawBMP, reqWidth, reqHeight, true);
the original bitmap is 512x512, thats also the size of rawBMP if i set inScaled to false.
The final bitmap only works on a device with less that 512 (my S2 with 480). On devices with higher res the final bmp size is reported as it should be, but the visible part of that image is the original size. So createScaledBitmap is creating a big bitmap but does not scale the original upwards.
If i set inScaled to true, i receive a rawBMP with LESS than original size but then the final bitmap seems to work but as you can imagine the quality suffers as it more than doubles in size.
On a Galaxy 10 tablet :
inScaled true : 512 original, 341 rawBMP, final 800 (density 160) final image is visible across the entire screen (height)
inScaled false: 510 original, 512 rawBMP, final 800 (density 240) final image visible part is still 512, the rest is filled with transparent, there was absolutely no scaling here.
I dont understand this behaviour. If i have a bmp with 512 pixels and want to scale it to 800 pixels and then just draw those pixels to the screen 1:1
edit
For arguments sake lets say i need the bitmap scaled this way, the code is a bit more than just this snippet
I have set the background of an Imageview in android (portrait mode) whose dimensions are 400 X 400 pixels. This image looks perfect on a 10" WXGA MDPI tablet (dimensions 800 X 1280). I happen to use the same layout with no modifications in the code on a 7" Nexus 7 (TVDPI 800 X 1280) tablet. The image is looking stretched. Can I programmatically find out the dimensions of the background in the layout on the Nexus 7? I want to mathematically scale down the dimensions of the background such that it looks like a square. I feel that the image background's width looks more than 400 pixels (say X) and the height is also more than 400 pixels (say Y). Also Y > X which makes it looks like a rectangle. How do I find X and Y programmatically in android on the Nexus 7? Please help. Is there a set formula to scale down or scale up the images so that the looks remain consistent on all android phones. Thank you
bellow code will let you take the width of the image and scale it accordingly
try {
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
SCREEN_WIDTH = displaymetrics.widthPixels;
Drawable ob = new BitmapDrawable(getResources(), bm);
float h = ob.getMinimumHeight();
float w = ob.getMinimumWidth();
float Hight = (h / w) *SCREEN_WIDTH;
imageView.setVisibility(View.VISIBLE);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, (int) Hight);
// imageView.setImageBitmap(bitmap);
params.setMargins(5, 5, 5, 5);
imageView.setLayoutParams(params);
int sdk = android.os.Build.VERSION.SDK_INT;
if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
imageView.setBackgroundDrawable(ob);
} else {
imageView.setBackground(ob);
}
} catch (Exception e) {
e.printStackTrace();
}
Rather than manually calculating the size of the image, have you tried using android:scaleType="centerInside" in your xml? You can set the size of the ImageView in dp units to keep a consistent size across different screens. The scaleType will ensure that the aspect ratio is maintained on scaling. You can also use android:scaleType="center" if you would like to prevent scaling and place different sized images in your drawables-xxx folders.
I want to increase size of image. i get image with this code BitmapFactory.decodeResource(getResources(), R.drawable.car);
But this image has different size on different screens, so i want to resize image according to screen size.
Of course you can scale:
Bitmap bm = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, filter);
Yes you can do this by using the following code snippets, the first section retrns the width and height of the current screen, the second section re-sizes the image.
// This will return the screen size in pixels
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
// Use this to bind and re-size the image
Bitmap car;
car = BitmapFactory.decodeResource(getResources(), R.drawable.car);
car = Bitmap.createScaledBitmap(car, width, height, true);
In android, one should store each resources in at least 3 formats, xhdpi, hdpi and mdpi
considering that you have stored a resource's copy in all the 3 formats at their respective folders.
res/drawable-mdpi/my_icon.png // bitmap for medium density
res/drawable-hdpi/my_icon.png // bitmap for high density
res/drawable-xhdpi/my_icon.png // bitmap for extra high density
The platform is enough smart to pick up right resources according to right screen size on its own.
I don't think you'll face any issues, if you have proper images in all 3 folders.
http://developer.android.com/guide/practices/screens_support.html
Look at http://square.github.io/picasso/ you can download, set where you want the image and resize it, just in one line. It also allows disk caching.
I'm writing an app and I want one of my activities to have a background,
I've read the android docs about supporting multiple resolutions etc,
but my designer is asking me what size the wallpapers should be and I do not want a lot of images for low,normal,high dpi in all the screen sizes.
What would be the most space efficient way to get a nice screen filling graphic background on all those screens?
Android is great and all, but I do not want to end up with a huge app since I need all sizes of images for everything.
One approach is to design for the densities and use the Icon Design guidelines for icons, and the dimensions specified in the "Range of Screens" area in the Supporting Multiple Screens guide for your background images.
For the background images be sure to take the status bar dimensions into consideration.
You can put your resources in the appropriate drawable-mdpi, drawable-hdpi, drawable-ldpi folders and they will be used appropriately.
What about having only one high resolution landscape background and using a "stretch" strategy for display?
mBackgroundGraphic = BitmapFactory.decodeResource(getResources(), R.drawable.background);
mBackgroundGraphic = getResizedBitmap(mBackgroundGraphic);
public Bitmap getResizedBitmap(Bitmap bm) {
int width = bm.getWidth();
int height = bm.getHeight();
int displayWidth = mDisplay.getWidth();
int displayHeight = mDisplay.getHeight();
float scaleWidth = ((float) displayWidth) / width;
float scaleHeight = ((float) displayHeight) / height;
// Create a matrix for the manipulation
Matrix matrix = new Matrix();
// Resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// Return the new stretched Bitmap
return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
}