Strange behavior of setTextSize (or maybe something else!) - android

I want to set the text size of my TextView to be proportional to screen width. I'm getting screen width using this code:
public static int getScreenWidth(Context context) {
if (SCREEN_WIDTH == 0) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics();
display.getMetrics(outMetrics);
SCREEN_WIDTH = outMetrics.widthPixels;
}
return SCREEN_WIDTH;
}
When I set the font size to be a proportion of screen width
textView.setTextSize(screenWidth / something);
I get different results on different devices, particularly Samsung GT-I9000 (android 2.3.3) and Nexus 7 (android 4.3).
However I managed to get it right by doing it this way:
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP,
Utils.pixelsToDip(context, screenWidth / something));
It's weird because basically it's like multiplying and dividing by same value.
Here is pixelsToDip method:
public static float pixelsToDip(Context context, float px) {
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float dp = px / (metrics.densityDpi / 160f);
return dp;
}
Does anyone knows what is the problem here? Maybe there is some faults in pixelToDip method, or I have a misunderstanding of size units?

Related

Android how to force screen density by default

By magnifying the screen by "Settings / Displays", I've found that my galaxy s9+'s screen density changes to 430 dpi to somewhere over 600dpi.
This makes the layout and images to change to xxhdpi to xxxhdpi.
If I have a textView which has the fixed size of 16dp,
it was 16 * 3 px in xxhdpi, and will become 16 * 4 px in xxxhdpi.
This makes my layout(of course texts from editTexts or textViews) to become much bigger since the actual density of the device never changes, which google(or the maker samsumg) intended for this function.
But I do not want this to happen in my app.
I've tried to fix it by doing this ;
Configuration configuration = activity.getResources().getConfiguration();
if (configuration.densityDpi != 430) {
configuration.densityDpi = 430;
}
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
WindowManager wm = (WindowManager) activity.getSystemService(activity.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
metrics.scaledDensity = configuration.densityDpi * metrics.density;
activity.getResources().updateConfiguration(configuration, metrics);
This forces the screen density to become 430 and works fine on galaxy s9+.
But, as a matter of fact, the default density will not be 430 for all devices.
How can I figure out the default density of the device which runs my application?
Or is there a configuration to ignore the magnify effect for my application?
This seems to work for me.
DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
int snap = 20;
float exactDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2;
float dpi = displayMetrics.densityDpi;
if (dpi - exactDpi > snap) {
int targetDpi = (int) (Math.ceil(exactDpi / snap) * snap);
Configuration config = activity.getResources().getConfiguration();
ErrorController.showMessage("adjustDisplayScale : " + config.densityDpi);
ErrorController.showMessage("targetDpi : " + targetDpi);
displayMetrics.densityDpi = targetDpi;
config.densityDpi = targetDpi;
displayMetrics.setTo(displayMetrics);
config.setTo(config);
activity.getResources().updateConfiguration(config, displayMetrics);
}

Supporting 9.6 inch and 10.1 inch android tablet

I have to support android devices with screen resolution 1280*800(in dp)(KitKat) and 1280 * 752(dp)(Lollipop). The first one is a 10.1 inch tablet and second one is 9.6 inch tablet.
I am using same layout for all device and using externalized dimension to adjust layout for different screen.
But I am not able to separate the device using "values-sw720dp" and "values-sw800dp". If I use sw800dp both of them use the dimen value from sw800dp dimen folder.
How can I give separate dimension for the two devices?
Get your device's screnn inches programmatically and appy dimension
public static double getDeviceInches(Activity activity) {
DisplayMetrics metrics = getMetrics(activity);
int widthPixels = metrics.widthPixels;
int heightPixels = metrics.heightPixels;
float widthDpi = metrics.xdpi;
float heightDpi = metrics.ydpi;
float widthInches = widthPixels / widthDpi;
float heightInches = heightPixels / heightDpi;
return getDiagonalInches(widthInches, heightInches);
}
private static double getDiagonalInches(float widthInches, float heightInches) {
double diagonalInches = Math.sqrt((widthInches * widthInches) + (heightInches * heightInches));
float roundedValue = (float) Math.round(diagonalInches);
return (double)roundedValue;
}
//From this, we can get the information required to size the display:
private static DisplayMetrics getMetrics(Activity activity) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
return metrics;
}
public static int convertDpToPx(Context context, int value) {
// Get the screen's density scale
final float scale = context.getResources().getDisplayMetrics().density;
// Convert the dps to pixels, based on density scale(0.5f is for rounding up value) (arrowWidth is 50dp)
int pxValue= (int) (value * scale + 0.5f);
return pxValue;
}
Put your above code in utility class and call getDeviceInches method from your acitivty or fragment class

Getting Screen Area in Dip(s) [duplicate]

I need to code the layout of the android widgets using dip/dp (in java files). At runtime if I code,
int pixel=this.getWindowManager().getDefaultDisplay().getWidth();
this return the screen width in pixels (px). To convert this to dp, I coded:
int dp =pixel/(int)getResources().getDisplayMetrics().density ;
This does not seem to be returning correct answer. I made the emulator of WVGA800 whose screen resolution is 480 by 800. When the run the emulator and let the code print the values of pixel and dp, it came to 320 in both. This emulator is 240 dpi whose scale factor would be 0.75.
As #Tomáš Hubálek mentioned;
Try something like:
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
OR
Try old answer:
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics ();
display.getMetrics(outMetrics);
float density = getResources().getDisplayMetrics().density;
float dpHeight = outMetrics.heightPixels / density;
float dpWidth = outMetrics.widthPixels / density;
I stumbled upon this question from Google, and later on I found an easy solution valid for API >= 13.
For future references:
Configuration configuration = yourActivity.getResources().getConfiguration();
int screenWidthDp = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier.
int smallestScreenWidthDp = configuration.smallestScreenWidthDp; //The smallest screen size an application will see in normal operation, corresponding to smallest screen width resource qualifier.
See Configuration class reference
Edit: As noted by Nick Baicoianu, this returns the usable width/height of the screen (which should be the interesting ones in most uses). If you need the actual display dimensions stick to the top answer.
2023 Answer simplified for Kotlin:
val widthDp = resources.displayMetrics.run { widthPixels / density }
val heightDp = resources.displayMetrics.run { heightPixels / density }
As one-liner:
val (height, width) = resources.displayMetrics.run { heightPixels/density to widthPixels/density }
For Jetpack Compose:
val (height, width) = LocalConfiguration.current.run { screenHeightDp.dp to screenWidthDp.dp }
How about using this instead ?
final DisplayMetrics displayMetrics=getResources().getDisplayMetrics();
final float screenWidthInDp=displayMetrics.widthPixels/displayMetrics.density;
final float screenHeightInDp=displayMetrics.heightPixels/displayMetrics.density;
You are missing default density value of 160.
2 px = 3 dip if dpi == 80(ldpi), 320x240 screen
1 px = 1 dip if dpi == 160(mdpi), 480x320 screen
3 px = 2 dip if dpi == 240(hdpi), 840x480 screen
In other words, if you design you layout with width equal to 160dip in portrait mode, it will be half of the screen on all ldpi/mdpi/hdpi devices(except tablets, I think)
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width_px = Resources.getSystem().getDisplayMetrics().widthPixels;
int height_px =Resources.getSystem().getDisplayMetrics().heightPixels;
int pixeldpi = Resources.getSystem().getDisplayMetrics().densityDpi;
int width_dp = (width_px/pixeldpi)*160;
int height_dp = (height_px/pixeldpi)*160;
Answer in kotlin:
context?.let {
val displayMetrics = it.resources.displayMetrics
val dpHeight = displayMetrics.heightPixels / displayMetrics.density
val dpWidth = displayMetrics.widthPixels / displayMetrics.density
}
In the new world of Compose on one line
val (height, width) = LocalConfiguration.current.run { screenHeightDp.dp to screenWidthDp.dp }
Get Screen Width and Height in terms of DP with some good decoration:
Step 1: Create interface
public interface ScreenInterface {
float getWidth();
float getHeight();
}
Step 2: Create implementer class
public class Screen implements ScreenInterface {
private Activity activity;
public Screen(Activity activity) {
this.activity = activity;
}
private DisplayMetrics getScreenDimension(Activity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics;
}
private float getScreenDensity(Activity activity) {
return activity.getResources().getDisplayMetrics().density;
}
#Override
public float getWidth() {
DisplayMetrics displayMetrics = getScreenDimension(activity);
return displayMetrics.widthPixels / getScreenDensity(activity);
}
#Override
public float getHeight() {
DisplayMetrics displayMetrics = getScreenDimension(activity);
return displayMetrics.heightPixels / getScreenDensity(activity);
}
}
Step 3: Get width and height in activity:
Screen screen = new Screen(this); // Setting Screen
screen.getWidth();
screen.getHeight();
This is a copy/pastable function to be used based on the previous responses.
/**
* #param context
* #return the Screen height in DP
*/
public static float getHeightDp(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
return dpHeight;
}
/**
* #param context
* #return the screnn width in dp
*/
public static float getWidthDp(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
return dpWidth;
}
If you just want to know about your screen width, you can just search for "smallest screen width" in your developer options. You can even edit it.
Your problem is with casting the float to an int, losing precision. You should also multiply with the factor and not divide.
Do this:
int dp = (int)(pixel*getResources().getDisplayMetrics().density);

Nexus 7 actual screen size

I built a simple application for Nexus 7. I used the following code to get the screen size in DP units.
this.getResources().getConfiguration().screenWidthDp;
this.getResources().getConfiguration().screenHeightDp;
where "this" is MainActivity context object.
I get these values: 600 dp for width and 888 dp for height.
Pixel density is tvdpi which is 213, and the ratio of dp to pixels is 1.33
I used this formula
pixels = dips * (density / 160)
which gives me for height
pixels = 888 * (213 / 160) = 1182.15.
I know that pixel size of the Nexus 7 screen is 800 x 1280. Where are the missing 100 pixels of height in this calculation? Or did I do something wrong?
Configuration.screenHeightDp() returns the dimensions of the available area of the screen.
Your calculated value, 1182, is close to the the height in pixels minus the navigation bar and status bar (1173) of the Nexus 7, in other words the resolution available for your app to use.
Full screen apps should be able to use the full 1280 resolution.
The following should give you the actual display size as a Point:
private Point getDisplaySize(Context context) {
if (Build.VERSION.SDK_INT >= 17) {
return getDisplaySizeMinSdk17(context);
} else if (Build.VERSION.SDK_INT >= 13) {
return getDisplaySizeMinSdk13(context);
} else {
return getDisplaySizeMinSdk1(context);
}
}
#TargetApi(17)
private Point getDisplaySizeMinSdk17(Context context) {
final WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
final Display display = windowManager.getDefaultDisplay();
final DisplayMetrics metrics = new DisplayMetrics();
display.getRealMetrics(metrics);
final Point size = new Point();
size.x = metrics.widthPixels;
size.y = metrics.heightPixels;
return size;
}
#TargetApi(13)
private Point getDisplaySizeMinSdk13(Context context) {
final WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
final Display display = windowManager.getDefaultDisplay();
final Point size = new Point();
display.getSize(size);
return size;
}
#SuppressWarnings("deprecation")
private Point getDisplaySizeMinSdk1(Context context) {
final WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
final Display display = windowManager.getDefaultDisplay();
final Point size = new Point();
size.x = display.getWidth();
size.y = display.getHeight();
return size;
}

How to determine the screen width in terms of dp or dip at runtime in Android?

I need to code the layout of the android widgets using dip/dp (in java files). At runtime if I code,
int pixel=this.getWindowManager().getDefaultDisplay().getWidth();
this return the screen width in pixels (px). To convert this to dp, I coded:
int dp =pixel/(int)getResources().getDisplayMetrics().density ;
This does not seem to be returning correct answer. I made the emulator of WVGA800 whose screen resolution is 480 by 800. When the run the emulator and let the code print the values of pixel and dp, it came to 320 in both. This emulator is 240 dpi whose scale factor would be 0.75.
As #Tomáš Hubálek mentioned;
Try something like:
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
OR
Try old answer:
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics ();
display.getMetrics(outMetrics);
float density = getResources().getDisplayMetrics().density;
float dpHeight = outMetrics.heightPixels / density;
float dpWidth = outMetrics.widthPixels / density;
I stumbled upon this question from Google, and later on I found an easy solution valid for API >= 13.
For future references:
Configuration configuration = yourActivity.getResources().getConfiguration();
int screenWidthDp = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier.
int smallestScreenWidthDp = configuration.smallestScreenWidthDp; //The smallest screen size an application will see in normal operation, corresponding to smallest screen width resource qualifier.
See Configuration class reference
Edit: As noted by Nick Baicoianu, this returns the usable width/height of the screen (which should be the interesting ones in most uses). If you need the actual display dimensions stick to the top answer.
2023 Answer simplified for Kotlin:
val widthDp = resources.displayMetrics.run { widthPixels / density }
val heightDp = resources.displayMetrics.run { heightPixels / density }
As one-liner:
val (height, width) = resources.displayMetrics.run { heightPixels/density to widthPixels/density }
For Jetpack Compose:
val (height, width) = LocalConfiguration.current.run { screenHeightDp.dp to screenWidthDp.dp }
How about using this instead ?
final DisplayMetrics displayMetrics=getResources().getDisplayMetrics();
final float screenWidthInDp=displayMetrics.widthPixels/displayMetrics.density;
final float screenHeightInDp=displayMetrics.heightPixels/displayMetrics.density;
You are missing default density value of 160.
2 px = 3 dip if dpi == 80(ldpi), 320x240 screen
1 px = 1 dip if dpi == 160(mdpi), 480x320 screen
3 px = 2 dip if dpi == 240(hdpi), 840x480 screen
In other words, if you design you layout with width equal to 160dip in portrait mode, it will be half of the screen on all ldpi/mdpi/hdpi devices(except tablets, I think)
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width_px = Resources.getSystem().getDisplayMetrics().widthPixels;
int height_px =Resources.getSystem().getDisplayMetrics().heightPixels;
int pixeldpi = Resources.getSystem().getDisplayMetrics().densityDpi;
int width_dp = (width_px/pixeldpi)*160;
int height_dp = (height_px/pixeldpi)*160;
Answer in kotlin:
context?.let {
val displayMetrics = it.resources.displayMetrics
val dpHeight = displayMetrics.heightPixels / displayMetrics.density
val dpWidth = displayMetrics.widthPixels / displayMetrics.density
}
In the new world of Compose on one line
val (height, width) = LocalConfiguration.current.run { screenHeightDp.dp to screenWidthDp.dp }
Get Screen Width and Height in terms of DP with some good decoration:
Step 1: Create interface
public interface ScreenInterface {
float getWidth();
float getHeight();
}
Step 2: Create implementer class
public class Screen implements ScreenInterface {
private Activity activity;
public Screen(Activity activity) {
this.activity = activity;
}
private DisplayMetrics getScreenDimension(Activity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics;
}
private float getScreenDensity(Activity activity) {
return activity.getResources().getDisplayMetrics().density;
}
#Override
public float getWidth() {
DisplayMetrics displayMetrics = getScreenDimension(activity);
return displayMetrics.widthPixels / getScreenDensity(activity);
}
#Override
public float getHeight() {
DisplayMetrics displayMetrics = getScreenDimension(activity);
return displayMetrics.heightPixels / getScreenDensity(activity);
}
}
Step 3: Get width and height in activity:
Screen screen = new Screen(this); // Setting Screen
screen.getWidth();
screen.getHeight();
This is a copy/pastable function to be used based on the previous responses.
/**
* #param context
* #return the Screen height in DP
*/
public static float getHeightDp(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
return dpHeight;
}
/**
* #param context
* #return the screnn width in dp
*/
public static float getWidthDp(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
return dpWidth;
}
If you just want to know about your screen width, you can just search for "smallest screen width" in your developer options. You can even edit it.
Your problem is with casting the float to an int, losing precision. You should also multiply with the factor and not divide.
Do this:
int dp = (int)(pixel*getResources().getDisplayMetrics().density);

Categories

Resources