Android Tutorial Supporting multiple screens - tips and best practices

Status
Not open for further replies.
This is an old tutorial. I recommend to instead watch the visual designer video tutorial: https://www.b4x.com/etp.html

There are several features in B4A that help you target Android phones and tablets with different screen sizes and resolutions. The purpose of this page is to collect tips and best practices that will help you create flexible layouts.

If you are not familiar with the designer script feature then please start with this tutorial: Designer Scripts Tutorial

- 'dip' units
It is very simple. You should always use 'dip' units when specifying the size or position of a view (control). This way the view's physical position and size will be the same on any device.
This is correct for both regular code and designer script.
B4X:
Button1.Width = 100 'WRONG!
Button1.Width = 100dip 'Good job!
Note that text size is measured in physical units. So you should not use 'dip' units with text size values.

- Use few layout variants
It is easy to create many variants. However it is very difficult to maintain a layout made of many variants. You should use the designer script feature and the anchoring feature to adjust your layout instead of creating many variants.

- Understand the meaning of scale (dots per inch)
There are many questions starting with "I have a device with 480x800 screen...". There is no meaning to these dimensions without the scale value.

A scale of 1.0 means that there are 160 dots (pixels) per inch.
The scale values can be one of the following values: 0.75, 1.0, 1.33, 1.5 and 2.
Most phones today have a scale of 1.5 (160 * 1.5 = 240 dots per inch) or 2.0.
Most tablets have a scale of 1.0.

- "Normalized" variants
Normalized variants are variants with a scale of 1.0.
The layout you create with the designer is scaled (not stretched or resized) automatically. This means that the layout will look exactly the same on two phones with the same physical size. The scale doesn't matter.
It is highly recommended to work and design your layout with normalized variants only.
For example a variant of 480x800, scale=1.5 matches the normalized variant: 320x533, scale=1.0 (divide each value by the scale value). Now it is easy to see that this device is slightly longer than the "standard" variant: 320x480, scale=1.0.

- Scaling strategy

Use the anchoring feature to anchor the views to their parents.
See this video example: Designer anchors - Video example
And: https://www.b4x.com/android/forum/threads/64112

Decide which views should be anchored and which views should "fill" the available space.

You can also use the designer script to make a view fill the available space:

This is done with SetTopAndBottom and SetLeftAndRight methods.
B4X:
'Make an EditText fill the available height between two buttons:
EditText1.SetTopAndBottom(Button1.Bottom, Button2.Top)

'Make a Button fill the entire PARENT panel:
Button1.SetLeftAndRight(0, Parent1.Width)
Button1.SetTopAndBottom(0, Parent1.Height)

Starting from v3.20 you can set the vertical or horizontal (or both) anchor to BOTH to achieve the same result.

- How to change the views size and text size?
Larger devices offer a lot more available space. The result is that even if the physical size of a view is the same, it just "feel" smaller.
Some developers use %x and %y to specify the views size. However the result is far from being perfect. The layout will just be stretched.
The solution is to combine the "dock and fill" strategy with a smart algorithm that increases the views size and text size based on the running device physical size.

Basic4android v2.20 introduces three new keywords that help with scaling the views size and text size:
AutoScale - Scales the specified view. The scale size, position and text size will be scaled based on the device physical size compared to the chosen variant size.
Example:
B4X:
AutoScale(Button1)
AutoScaleAll - Scales all layout views.
AutoScaleRate - Sets the scale rate. This is a number between 0 to 1. The default value is 0.3.
A value of 0 means no scaling at all.
Value of 1 means that the scale factor is exactly proportional to the device physical size. The result is a "stretched" UI.

The recommended process is to first scale all views and then adjust their positions as required.

If done properly this saves the need to create many variants. Your layout will look good on all devices.

Test your layout

You can test your layout with a real device, an emulator, the abstract designer and the UI Cloud service (Tools - Send to UI Cloud).
 
Last edited:

GuyBooth

Active Member
Licensed User
Longtime User
What am I missing?

I have read through the threads and tutorials on multiple device setups, but have not been able to get past a basic problem.
I built my app based on an S3, using layouts in the designer, without any scripts. It is portrait only, looks good in the emulator (320 x 480) and on my S3.
Firstly, As soon as I uncomment the AutoScaleAll, the layout "Jumps", and I lose the small amount of padding I had included on the right hand side. The only way I have found to get it back is to add the line "AutoScaleRate(0.0)". No other value will get it back.
Secondly, I added a Variant for a 10.1 tablet, portrait mode. When I apply the layout to this variant, the layout only takes up about 1/4 of the screen. To fill the screen I have to set the AutoScaleRate to 0.75. But if I set it to 0.75, the S3 layout is wrong.

This doesn't match anything I've read. This is just a simple panel layout, there's nothing extraordinary about it. Everyone else seems happy with it, so I must be doing something wrong? Or am I not understanding what it is designed to do? I can put the scaling into the device specific scripts, but I didn't think it belonged there.
 

andrewtheart

Member
Licensed User
Longtime User
Hi Erel:

How do I access AutoScaleAll and AutoScale? They are not recognised by the IDE.

Sorry if I should have opened a different thread for this, but it seems relevant to this guide.
 

LucaMs

Expert
Licensed User
Longtime User
There are several features in Basic4android (and Designer4android) that help you target Android phones and tablets with different screen sizes and resolutions. The purpose of this page is to collect tips and best practices that will help you create flexible layouts.

If you are not familiar with the designer script feature then please start with this tutorial: Designer Scripts Tutorial

- 'dip' units
It is very simple. You should always use 'dip' units when specifying the size or position of a view (control). This way the view's physical position and size will be the same on any device.
This is correct for both regular code and designer script.
B4X:
Button1.Width = 100 'WRONG!
Button1.Width = 100dip 'Good job!
Note that text size is measured in physical units. So you should not use 'dip' units with text size values.

- Use only a few layout variants
It is easy to create many variants. However it is very difficult to maintain a layout made of many variants. You should use the designer script feature to adjust (or fine tune) your layout instead of creating many variants.

- Understand the meaning of scale (dots per inch)
There are many questions starting with "I have a device with 480x800 screen...". There is no meaning to these dimensions without the scale value.

A scale of 1.0 means that there are 160 dots (pixels) per inch.
The scale values can be one of the following values: 0.75, 1.0, 1.5 and 2.
Most phones today have a scale of 1.5 (160 * 1.5 = 240 dots per inch).
As far as I know, all tablets today have a scale of 1.0.

- "Normalized" variants
Normalized variants are variants with a scale of 1.0.
The layout you create with the designer is scaled (not stretched or resized) automatically. This means that the layout will look exactly the same on two phones with the same physical size. The scale doesn't matter.
It is highly recommended to work and design your layout with normalized variants only.
For example a variant of 480x800, scale=1.5 matches the normalized variant: 320x533, scale=1.0 (divide each value by the scale value). Now it is easy to see that this device is slightly longer than the "standard" variant: 320x480, scale=1.0.

- Scaling strategy
A useful example is available here: http://www.b4x.com/forum/designer-layouts/17460-useful-tempate-1-a.html

You should decide what will happen with your layout when it runs on a larger device.
Usually some views will be docked to the edges. This can be done easily with the designer script.
For example, to dock a button to the right side:
B4X:
Button1.Right = 100%x

Some views should fill the available area.
This is done with SetTopAndButton and SetLeftAndRight methods.
B4X:
'Make an EditText fill the available height between two buttons:
EditText1.SetTopAndBottom(Button1.Bottom, Button2.Top)

'Make a Button fill the entire PARENT panel:
Button1.SetLeftAndRight(0, Parent1.Width)
Button1.SetTopAndBottom(0, Parent1.Height)

- How to change the views size and text size?
Larger devices offer a lot more available space. The result is that even if the physical size of a view is the same, it just "feel" smaller.
Some developers use %x and %y to specify the views size. However the result is far from being perfect. The layout will just be stretched.
The solution is to combine the "dock and fill" strategy with a smart algorithm that increases the views size and text size based on the running device physical size.

Basic4android v2.20 introduces three new keywords that help with scaling the views size and text size:
AutoScale - Scales the specified view. The scale size, position and text size will be scaled based on the device physical size.
Example:
B4X:
AutoScale(Button1)
AutoScaleAll - Scales all layout views.
AutoScaleRate - Sets the scale rate. This is a number between 0 to 1. The default value is 0.3.
A value of 0 means no scaling at all.
Value of 1 means that the scale factor is exactly proportional to the device physical size. The result is a "stretched" UI.

The recommended process is to first scale all views and then adjust their positions as required.

If done properly this saves the need to create many variants. Your layout will look good on all devices.

Test your layout

You can test your layout with a real device, an emulator, the abstract designer and the UI Cloud service (Tools - Send to UI Cloud).


I start to think about being obtuse, but just do not understand (especially I do not understand why the layout that I made some time ago do not fit any display).

And I had good grades in geometry, ever since elementary school!

But all the "work" should not be resolved by simple linear equations?

Button1.Height(50dip) : Button1.NewHeight(Xdip) = Screen.Height(480dip) : Screen.NewHeight(960dip)

then: Button1.NewHeigh(Xdip) = (Button1.Height(50dip) * Screen.NewHeight(960dip)) / Screen.Height(480dip)

then: (50dip * 960dip) / 480dip = 100dip!

What the hell does "AutoScaleRate - Sets the scale rate. This is a number between 0 to 1. The default value is 0.3." ... is it a random number?

Sorry for my nerves and my bad English.
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
But all the "work" should not be resolved by simple linear equations?
No. The purpose of AutoScaleAll is not to "stretch" the layout. If you set the scale rate to 1 then it will just stretch the layout (more or less).

AutoScaleAll slightly increases (or decreases) the size of all views, and the text size of all views. You can see how AutoScaleAll works in Klaus AutoScale code module.
 

LucaMs

Expert
Licensed User
Longtime User
No. The purpose of AutoScaleAll is not to "stretch" the layout. If you set the scale rate to 1 then it will just stretch the layout (more or less).

AutoScaleAll slightly increases (or decreases) the size of all views, and the text size of all views. You can see how AutoScaleAll works in Klaus AutoScale code module.


I'm afraid it's because of my poor English.

The purpose of AutoScaleAll is or is not to obtain the same proportions of views on any display?

Ie: my device is 480x800, buttonX is 50x100 and by means of AutoScale you want to get that on a display of 240x400 buttonX automatically become 25x50 (taking into account also the factor dpi)?

If not, I really do not understand. If so, I do not understand the functionality of AutoScaleRate: perhaps to decrease or increase the size and not to make them "compatible"?

I will study carefully the Klaus module, but at first glance, it seems to do the same work than one i tried to develope, without getting the results that I wanted (although it seemed to work with B4A 1.80).

Thank you for trying to explain it to me, Erel.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
The purpose of AutoScaleAll is or is not to obtain the same proportions of views on any display?
No. The tablet layout should not look like the an enlarged phone layout. If you do want to create such a layout then you should set all the dimensions with %x and %y.

my device is 480x800
480x800 is meaningless without the device scale. I guess that the scale is 1.5. In that case the phone "normalized" layout is 320x533, scale=1.0. This means that it is a bit taller than the standard layout of 320x480, scale=1.0.

AutoScaleAll works correctly. I recommend you to use the UI Cloud and run some tests to better understand what it supposed to do.
 

corwin42

Expert
Licensed User
Longtime User
I think there is many confusion about what AutoScaleAll really does. AutoScaleAll has nothing to do with scaling the same layout to different display resolutions and sizes. This is done automatically by Android with the scale factor of the display. But because of different aspect ratios of the displays you will have to position the elements with the help of %x and %y values.

The only thing AutoScaleAll does is make the elements (buttons etc) on a tablet slightly larger than on a phone to make them look better. Nothing else.
 

LucaMs

Expert
Licensed User
Longtime User
No. The tablet layout should not look like the an enlarged phone layout. If you do want to create such a layout then you should set all the dimensions with %x and %y. <--- this is all i need! for text size too. And on tablet too!


480x800 is meaningless without the device scale. I guess that the scale is 1.5. In that case the phone "normalized" layout is 320x533, scale=1.0. This means that it is a bit taller than the standard layout of 320x480, scale=1.0.
I know and wrote: "my device is 480x800, buttonX is 50x100 and by means of AutoScale you want to get that on a display of 240x400 buttonX automatically become 25x50 (taking into account also the factor dpi)?"

AutoScaleAll works correctly. I recommend you to use the UI Cloud and run some tests to better understand what it supposed to do.

I'll try to use x but I guess that's how to do testing on emulators

thanks

PS expand to read all, please
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
I think there is many confusion about what AutoScaleAll really does. AutoScaleAll has nothing to do with scaling the same layout to different display resolutions and sizes. This is done automatically by Android with the scale factor of the display. But because of different aspect ratios of the displays you will have to position the elements with the help of %x and %y values.

The only thing AutoScaleAll does is make the elements (buttons etc) on a tablet slightly larger than on a phone to make them look better. Nothing else.


"This is done automatically by Android with the scale factor of the display. But because of different aspect ratios of the displays you will have to position the elements with the help of %x and %y values."

You mean that my views on 480x800-1.5 automatically (S.O.) would be perfect on a 240x400-1.5? But even that (aspect-ratio) if the second display was 200x400-1.5, using % should already fit everything, all kinds of views and texts! If so, because I would need to... "...slightly larger..." ?

I give up. I'll never understand. Maybe writing less and doing more tests.

Many thanks all
 

LucaMs

Expert
Licensed User
Longtime User
I use 2 layouts in this project (is a test). The first is used only as Main to launch the second one, but:

layMainTest1 (NEW) built using b4a 3.00 - 480x800(1.5) (NOT "normalized")
layRicordami (OLD) built using b4a 1.80 - 480x800(1.5) (NOT normalization, obv.)

MY STEPS:

load project
open the designer
load "layMainTest1"
load "layRicordami"
select (try to load) "layMainTest1" - msg : "Do you want to save designer changes?"
but i changed nothing!
That message does not appears when i load "layRicordami" having layMainTest1 loaded.
I answered NO

Running only the (empty) scripts in the Designer:

Without AutoScaleAll "actived", nothing changes. Activing it, both layouts appear stretched (on the Abstract Designer)


Running project:
on my device (480x800-[240] S.O. 4.0.4:

Without AutoScaleAll the layout appears vertically stretched: WHY?

Using instead:
Scale.SetReferenceLayout(480, 800, 1.5)
Scale.ScaleAll(Activity, True)
Texts are wrong or disappeared. Adding Scale.SetRate(1) too.
[UPDATED] Klaus, a strange thing happens.

The contents of the listview seems to disappear but I launched the program with debugging, and if I keep pressing F8 until the end of scaleall ... this does not happen, the listview is ok.

Maybe you should put an .Invalidated somewhere? But why this happens at 480x800... and not 480x640?




on an emulator: 480x640-[160] API L.19:

Scale.SetReferenceLayout(480, 800, 1.5)
Scale.ScaleAll(Activity, True)
Texts are right.
 

Attachments

  • lm Test1.zip
    13.4 KB · Views: 622
Last edited:

klaus

Expert
Licensed User
Longtime User
Using instead:
Scale.SetReferenceLayout(480, 800, 1.5)
Scale.ScaleAll(Activity, True)
Texts are wrong or disappeared. Adding Scale.SetRate(1) too.
Sorry, there is an error in the Scale module when Width or Height properties are lass than 0.
I wasn't aware that the default values for the Label Width and Height in a ListView are -1.

Try the attached amended version.
Scale module version 1.4
 

Attachments

  • lm Test2.zip
    13.4 KB · Views: 634

LucaMs

Expert
Licensed User
Longtime User
Klaus, a strange thing happens.

The contents of the listview seems to disappear but I launched the program with debugging, and if I keep pressing F8 until the end of scaleall ... this does not happen, the listview is ok.

Maybe you should put a. Invalidated somewhere?
Sorry, there is an error in the Scale module when Width or Height properties are lass than 0.
I wasn't aware that the default values for the Label Width and Height in a ListView are -1.

Try the attached amended version.
Scale module version 1.4



LOL

I was writing...

SOLVED:

Else If GetType(v) = "anywheresoftware.b4a.objects.ListViewWrapper$SimpleListView" Then
' test if the view is a ListView
' if yes scales the internal views
Dim ltv As ListView
ltv = v
DoEvents <--- WITH THIS
 

LucaMs

Expert
Licensed User
Longtime User
Sorry, there is an error in the Scale module when Width or Height properties are lass than 0.
I wasn't aware that the default values for the Label Width and Height in a ListView are -1.

Try the attached amended version.
Scale module version 1.4


Both solutions work, but my is empirical, your is technical. Now I have no way to understand it, my head smokes (ehm that is, it gives off smoke) :)

Thanks
 

LucaMs

Expert
Licensed User
Longtime User
Sorry, there is an error in the Scale module when Width or Height properties are lass than 0.
I wasn't aware that the default values for the Label Width and Height in a ListView are -1.

Try the attached amended version.
Scale module version 1.4


As I wrote in a post, I had also developed a module like yours.
It's not bad, but do not fit the images, I have not studied them enough.

But the biggest problem is in TextSize.

You multiply by cScaleX, I did it (instinctively!) for a factor Y.

Still in the project lmTest1, I have tried both and also their average, on a 480x600-240 emulator,
but none of the three solutions is optimal. X, for that display, is the worse.

I'll start studying typography ... I MUST find the solution :)

(If I can afford a few tips (useful for all of us :) ) ...
You could optimize the code with Scale.ScaleAll (Activity, ScaleMode, True)?
Use a variable instead of calling GetType always.
I do not allow myself to edit your useful module)


Best regards
 
Status
Not open for further replies.
Top