Android: intelligent way of sizing UI components for all screens? - android

I've been having trouble finding a generic way to size UI components so they look good on all devices. I have some ideas but could use any feedback on them or suggestions for better strategies.
Take a simple situation like a dialog with a single EditText which a user will enter a fairly long String into. On smaller devices I would like the EditText to be as wide as possible (because its likely that the maximum size possible on these devices isn't all that large). However, if I set the width of this EditText to FILL_PARENT, the dialog will look silly on tablets, because it will be much, much wider than it needs to be. Also, setting the width to some hard coded DIP value, like 500, isn't great because it doesn't maximize available space on smaller devices, won't expand to take up additional space if the device is rotated, and could be an issue on devices narrowers than 500 DIP.
What I would like to do is approximate the behavior FILL_PARENT by hardcoding the width of the EditText to of the screen's width, but to prevent the EditText from growing to wide on large devices by not allowing the width to be larger than some DIP value, like 500.
I have encountered 2 problems with this strategy:
1: If I set the EditText's width to the screen's width, the EditText doesn't fit on the screen (as shown in the below image). I fixed this by setting the width to 90% of screen width, but this is questionable (what if the device's dialogs have larger margins around them?).
2: My app sets android:configChanges flag in its application manifest such that dialogs don't get dismissed on rotation. But this means that dialogs don't get resized on rotation. So, if the device rotates from landscape to portrait while the dialog is visible, then the EditText will be too wide for the screen; and if it goes the opposite direction, then the EditText will not take up all the available space. I guess this would be fixable by relaying out my dialog on rotate, but it seems painful/ugly for my Activity to have to keep track of the visible Dialog and force it to relayout on rotation (particularly because each dialog would then have re-populate any field values when after relayout).
So, any thoughts on improving my solution, or on alternatives?
class SampleDialog extends Dialog
{
public SampleDialog(Context context)
{
super(context);
setContentView(getContentView());
}
private View getContentView()
{
TextView label = new TextView(getContext());
label.setText("A label:");
int fiveHundredDips = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 500, activity.getResources().getDisplayMetrics());
Display display = getWindowManager().getDefaultDisplay();
float width = .9f * display.getWidth();
Math.min(width, fiveHundredDips);
LinearLayout.LayoutParams linearParams = new LinearLayout.LayoutParams((int) width, LinearLayout.LayoutParams.WRAP_CONTENT);
EditText editText = new EditText(getContext());
editText.setLayoutParams(linearParams);
LinearLayout layout = new LinearLayout(getContext());
layout.addView(label);
layout.addView(editText);
return layout;
}
}
}

You could use different folders in your resources folder, which define different qualifiers for different devices. You can then specify say on a small device you would like a dimension to be 'x' big, and on a tablet to be 'x' big.
Another solution would be to programatically detect if the device is a Tablet, and perform actions based on that.
I would recommend you look into resource qualifiers, which are a very powerful tool in Android.

Take a look at the android:minWidth property. It's available on TextView as well as others. It seems the minWidth (set using dip units) would be a good option for you. You can make the text boxes large enough but not too large.
These values can be set programmatically. You might also find the following code useful in determining what values you want to use for the minWidth:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
Log.d("density", metrics.density);
Log.d("width dip", (int) (metrics.widthPixels / metrics.density));
Log.d("height dip", (int) (metrics.heightPixels / metrics.density));

Related

Scaling given view and screen width behaviour to be same for different screen sizes

I have a requirement to make a particular button view.
As per the requirements, the max width of button text is specified as 48px, when the screen width size is given as 360px. But since the resolution can be different for different devices, to satisfy the requirement, I am using
screenWidth = context.resources.displayMetrics.WidthPixels
and then using the ratio (screenWidth*48)/360 to set the maxWidth of the button text like so:
button.title.maxWidth = (screenWidth * 48) / 360
My question is that how can I better satisfy the requirements, without using these ratios? Also is there a way to use dps instead of pixels in this situation?
Use Autosizing TextViews.
Read the developers guide for more information.

Restricting an app's screen size based on a phone's resolution

I am developing an application in android studio which is displayed correctly on 1080x1920 phones(and lower) but for phones like mine(s8) the app gets messed up and i can't find a way to fix the app's ui for every screen size. So, i was wondering if there is a way to restrict the app's screen size to 1080x1920(with let's say black bars at the top & bottom of the screen) even if a phone's dimensions are bigger than that(this is automatically done in games that are not compatible with s8). Do you guys know a way of achieving this effect?
It's possible, you can set your parent View to exact size given the aspect ratio you want (1080x1920 scales down to 9x16). For this you'd have to programmatically call the View (or create it in code) and set width and height in pixels with LayoutParams:
yourView.setLayoutParams(new LayoutParams(width, height));
for the weight and height you'd want the actual screen dimensions, which you can get from the WindowManager:
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point point = new Point();
display.getSize(point);
int x = point.x;
int y = point.y;
Here you get the x and y, which hold your actual screen size (in pixels). Then, you decide on which one should be reduced (in order to maintain 9x16 aspect ratio) and set your parent View's LayoutParams as mentioned above.
Finally, your set your content View as your parent View:
setContentView(yourView);
That's about it. Hope that helps.
EDIT: Since you've mentioned games, I can tell you that there (if your use Android SDK) you have your SurfaceView as a parent View, the procedure is same.

how to get android screen size programmatically, once and for all?

How can I find out my screen size programmatically,
in the units used by touch events
and View measurement/layout?
In other words, I want the coordinates
of the bottom-right corner of the screen,
in the coordinate system used by touch events'
getRawX()/getRawY() and View.getLocationOnScreen().
I'm hesitant to call the desired event/view units "pixels"
since evidently there are several notions of "pixels"
on my phone in various modes,
and they're not adding up to a consistent story.
I see this has been asked and answered a lot
on stackoverflow and elsewhere,
but none of the answers actually work on my phone
(droid 4, android 4.1.2) in all modes:
Can I find out width of screen programmatically in android app?
How do I get the ScreenSize programmatically in android
Android set View visibility programmatically based on screen size
How do I get the height of the screen in Android?
Get the screen height in Android
https://groups.google.com/forum/#!topic/android-developers/IpxnfvDFrpc
http://shuklaxyz.blogspot.com/2012/02/how-to-programmatically-figure-out.html
http://coderock.net/how-to-determine-screen-resolution-programmatically/
http://www.codeproject.com/Tips/313848/Get-actual-screen-size-for-the-application-layout
Android and setting width and height programmatically in dp units
http://www.simplecodestuffs.com/how-to-get-screen-dimensions-programmatically-in-android/
http://www.androidsnippets.com/get-size-and-orientation-of-the-screen
http://grokbase.com/t/gg/android-developers/127aatfqb6/how-to-determine-screen-resolution-programmatically
(wow!)
This is for library code that needs to work
regardless of whether the app is in "screen compatibility mode"
(i.e. targetSdkVersion<=10 in the manifest) or not,
and I also don't want to make any assumptions
about the position, size, or existence of the status bar
or any other similar screen decorations.
Here are the facts:
my phone (a droid 4 running android 4.1.2)
has 540x960 physical pixels,
i.e. little colored glowing dots.
the size of the screen in the desired units,
from looking at touch events and View measurements, is
360x640 when the app is in screen compat mode,
540x960 when the app is not in screen compat mode.
These are the numbers I need to find programmatically,
without mucking with touch events or Views to find them,
but I'm having extreme difficulty finding any API
that will return these numbers.
Display and DisplayMetrics objects obtained
in various ways all claim the screen size
is 540x960 "pixels"
(whether in screen compat mode or not).
To be specific, the following all say 540x960 all the time:
DisplayMetrics.{width,height}Pixels,
Display.getSize(),
Display.getRealSize(),
Display.get{Width,Height}(),
Configuration objects obtained in various ways
all say screen{Width,Height}Dp = 360x614
(whether in screen compat mode or not).
I don't believe that represents the whole screen,
since the aspect ratio is wrong.
(I think it's the whole screen
minus the status bar; I need the whole screen.)
I think it's safe to say that the whole screen is 360x640 dp,
though I don't know any API that returns that 640.
DisplayMetrics obtained in various ways
say the "density" is
1.0f when in screen compat mode,
1.5f when not in screen compat mode.
The activity's
getWindow().getAttributes().{width,height}
isn't helpful since it typically contains MATCH_PARENT
rather than actual sizes.
But I can apparently get the desired answer from an activity's
getWindow().getDecorView().getMeasured{Width,Height}()
(which is actually surprising since
the activity's window's decorView
doesn't look like it's taking up the whole screen;
it looks like it's taking up the screen minus the status bar).
But I don't want to rely on this because if the window ever gets resized
(soft keyboard appearing? someone calling window.setAttributes()?
or maybe I'm not in an Activity at all),
this is clearly going to be all wrong.
I understand the following formula is supposed to hold:
pixels = dp * density
That seems to agree with all the reported numbers ((3),(4),(5) above)
when not in screen compatibility mode:
540x960 = 360x640 * 1.5
But in screen compatibility mode it doesn't add up:
540x960 != 360x640 * 1
So, something is awry.
The simplest explanation, I think,
is that the methods listed in (3) above
are simply giving the wrong answer for "pixels" when
in screen compat mode-- that is, they were intended
to return 360x640 "pixels" but they are wrongly returning 540x960 instead.
But there may be other ways of looking at it.
In any case, getting the desired numbers regardless of mode,
from the above puzzle pieces, is certainly a tricky puzzle.
I have found a way that seems to work on my phone in both modes,
but it is extremely circuitous,
and it relies on two assumptions that still seem rather shaky
(as described in the code comments below).
Here is my recipe:
/** get screen size in "pixels", i.e. touchevent/view units.
* on my droid 4, this is 360x640 or 540x960
* depending on whether the app is in screen compatibility mode
* (i.e. targetSdkVersion<=10 in the manifest) or not. */
public void getScreenSizePixels(int widthHeightInPixels[/*2*/])
{
Resources resources = getResources();
Configuration config = resources.getConfiguration();
DisplayMetrics dm = resources.getDisplayMetrics();
// Note, screenHeightDp isn't reliable
// (it seems to be too small by the height of the status bar),
// but we assume screenWidthDp is reliable.
// Note also, dm.widthPixels,dm.heightPixels aren't reliably pixels
// (they get confused when in screen compatibility mode, it seems),
// but we assume their ratio is correct.
double screenWidthInPixels = (double)config.screenWidthDp * dm.density;
double screenHeightInPixels = screenWidthInPixels * dm.heightPixels / dm.widthPixels;
widthHeightInPixels[0] = (int)(screenWidthInPixels + .5);
widthHeightInPixels[1] = (int)(screenHeightInPixels + .5);
}
Is there any better/cleaner way
to find the size of the screen??
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Now you can measure your screen size in pixel which is a better measurement unit than centimeter because all the buttons ,textviews etc.. are measured in this unit. That what I use normally
By using the DisplayMetrics you can get height and width of the screen of any device. Note that width and height are sensitive to rotation.
here is the code.
DisplayMetrics metrics;
int width = 0, height = 0;
In your onCreate Method
metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
height = Math.min(metrics.widthPixels, metrics.heightPixels); //height
width = Math.max(metrics.widthPixels, metrics.heightPixels); //width
Try this.
In your #6, you note that the DecorView seems to provide what you want, but you don't think it is realiable. I believe that is as close as you can come. According to the documentation for Window.getDecorView:
Retrieve the top-level window decor view (containing the standard window frame/decorations and the client's content inside of that), which can be added as a window to the window manager.
The status bar is part of the window decoration mentioned there. Software keyboards and other overlays will receive their own window, so they shouldn't interfere with the relevant values. It is correct that isn't precisely the display metrics, but if your application is full screen it should always be the full screen size filtered through the compatibility translator. Other applications won't have access to your application's Window, so there is no need to worry about some other app changing the attributes while you aren't looking.
Hope that helps.
I should put this as a comment but I don't have the rep for that.
This may not be a totally correct answer but I got my dimensions when I switched the height and width in the DisplayMetrics method.
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
width = metrics.heightPixels;
height = metrics.widthPixels;
as I said this may not be correct but I don't know why but it worked for me.
If you are using api level 17 or higher check out getRealMetrics and getRealSize in Display
For api level 14,15 & 16 look here
I've used Pythagoras theorem to find the diagonal size of Android phone/tablet screen, same principal can be applied to iPhone or Blackberry screen.
DisplayMetrics met = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(met);// get display metrics object
String strSize =
new DecimalFormat("##.##").format(Math.sqrt(((met.widthPixels / met.xdpi) *
(met.widthPixels / met.xdpi)) +
((met.heightPixels / met.ydpi) * (met.heightPixels / met.ydpi))));
// using Dots per inches with width and height
Pythagoras must have been a genios, he knew smart phone programming so many years ago :p
I use the below method to get the width of the device :
public static int getDeviceWidth(Context context){
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int width=display.getWidth();
return width;
}
I think this function will help you to simply get the width and height of your android screen size. The function returns the width and height as an array of integers as shown below
private int[] getScreenSIze(){
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int h = displaymetrics.heightPixels;
int w = displaymetrics.widthPixels;
int[] size={w,h};
return size;
}
and on your create method output your width and height as shown below
int[] screenSize= getScreenSIze();
int width=screenSize[0];
int height=screenSize[1];
screenSizes.setText("Phone Screen sizes \n\n width = "+width+" \n Height = "+height);
Download source code and test it on your android studio
This works for me:
int width = Resources.getSystem().getDisplayMetrics().widthPixels;
int height = Resources.getSystem().getDisplayMetrics().heightPixels;
I used this to update width of my items in a horizontal recycler view.(according to my requirement)
Hope it helps someone!
For this solution in Kotlin, try this:
private fun yourFunction(){
val metrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(metrics)
val width = metrics.widthPixels
val height = metrics.heightPixels
}

Spinning wheel half way off screen android

I have a requirement for a spinning wheel graphic that is half-off the bottom of the screen and then animates (spins) when users take action.
I can place the wheel graphic off the screen by calling:
Animation animation = new TranslateAnimation(Animation.ABSOLUTE,0,Animation.ABSOLUTE, 0,Animation.ABSOLUTE,wheelPos, Animation.ABSOLUTE, wheelPos);
animation.setDuration(1);
animation.setFillAfter(true);
wheel.startAnimation(animation);
That is fine, I can set the width to whatever I want via LayoutParams.
But how can I make the wheel the same size for different screen sizes? For example, I need to place the wheel Y pixels off the screen (wheelPos variable) above, but on a Nexus S versus Nexus 7 how can I calculate an appropriate Y value?
The same goes for width, how can I calculate a width so that it appears exactly the same size?
I note the Catch android app https://catch.com/ - if I expand their wheel menu, it's size is identical on the Nexus S versus Nexus 7 - how might they have achieved that?
Developer of Catch here. Thanks for the email pointing us to your question here.
For our wheel menu, its width and positioning are specified within XML layout files, using dp/dip (density-independent pixels). For a given dimension, say, 300dp, it will appear the same size on any Android device; the system will scale that value depending on whether the device's screen is low, medium, high, xhigh, etc. density.
So, set your width and Y offset using dp and you should be good to go. You can do it completely programmatically if you wish:
final int wheelWidth = 300;
final int wheelYoffset = 64;
DisplayMetrics dm = getResources().getDisplayMetrics();
final float scaledWheelWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, wheelWidth, dm);
final float scaledWheelYoffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, wheelYoffset, dm);
I personally find it much easier to maintain all of my UI dimensions as resources. In res/values/dimens.xml I'd add:
<dimen name="wheel_width">300dp</dimen>
<dimen name="wheel_y_offset">64dp</dimen>
and then if I needed to use those dimensions in code, I can grab them with a one-liner:
final float scaledWheelWidth = getResources().getDimensionPixelSize(R.dimen.wheel_width);
final float scaledWheelYoffset = getResources().getDimensionPixelOffset(R.dimen.wheel_y_offset);
Using dp for layout is one of the keystone principles of making an Android UI that scales nicely to different devices, and is especially critical for anything you expect your user to touch/interact with, since you can size your hit targets during development and rest assured that they will be physically similarly-sized no matter what device your app is running on.

How to make my android component as size dependent for various display screen sizes?

I am making an android component which allows user to pick date from it. It can be helpful for developer who wants user to select date in his app. In my basic view, I have TextView where date from pop up will be populated into it and I have a button beside TextView. When a User clicks on the button, my component gets popped out and displays Dates. The component gets pops out in a Popup window and shows dates as month view and user also can switch from next-previous months, next-previous years just like we do in Calendar. Check the Image.
http://s15.postimage.org/ujw8py60b/stackoverflow.jpg (Sorry, I couldn't upload an image here because I am not allowed as I am new User here)
Each date is a TextView with the width of 35 and height as 30 set by me. DaysDisplayBar is also of some size set by me. So this component's whole width is 245 and height is around 200. Which is for mobile screen size.
I want to make this component as size dependent for various screen display sizes. For e.g. If it is being viewed on Tablet or Pad, it should be bigger in size than what its size on mobile phone screen. That is, For various displays its size should be changed to some value like max 1/3 of display size or like that something.
What can be the solution for this? According to me, some mathematics is needed here, some formula, equations etc. how about Parabola? Please help, I am dumb in maths totally. Thanks! :D
"Each date is a TextView with the width of 35 and height as 30 set by me. DaysDisplayBar is also of some size set by me. So this component's whole width is 245 and height is around 200. Which is for mobile screen size."
^^ is the problem. Sizes should be defined relative to the layout, not absolute. For example, the calendar has 7 columns (one for each day). Instead of making each one 35px, make each 1/7th of the screen.
SO:
I am assuming a DaysDisplayBar is a row containing 7 TextViews (one for each day). If that is true, why not call it a Week? Either way, The trick is in layout_wieght. Make all elements fill_parent, and all with the same weight of 1. This will evenly distrubate all elements in the parent. In your case, the parent is a DaysDisplayBar.
SO:
set DaysDisplayBar attribute `layout_width="fill_parent"
For each TextViewset attribute layout_width="fill_parent" ANDlayout_weight="1"`
hope that helps!
First of all, make sure you use density pixels (dip) instead of pixels.
Second, you can get the screen width and height, and from there, calculate your component size.
You can get the screen dimensions using the Display class getSize() method:
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point screenSize = new Point();
display.getSize(screenSize);
int screenWith = screenSize.x;
Or you can get the parent view dimensions:
MarginLayoutParams params = (MarginLayoutParams)parentView.getLayoutParams();
int padding = parentView.getPaddingLeft() + parentView.getPaddingRight();
int margin = params.leftMargin + params.rightMargin;
int measuredWidth = parentView.getMeasuredWidth() - padding - margin;
That way you know how much space you have inside the parent view element for your component.
Remember to convert any hard coded value to dip, you can do it this way:
public static int getDensitySize(float size) {
float density = getResources().getDisplayMetrics().density;
return (int)(density * size);
}
You do all of this from your onMeasure method to set your view size, and later on the onDraw you'll use this measure to draw your component.

Categories

Resources