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:

salmander

Active Member
Licensed User
Longtime User
Thanks Erel. Here is my scenario, my app works fine on my phone, but when I installed my app on a low resolution device the views are properly aligned but the text in it is only appearing partially, which makes sense, as the screen dpi and resolution is small. So I am trying to resize the text-size based on the view height and width.
Can you please provide a sample code?
 

CarlM

Member
Licensed User
Longtime User
Variant Specific Scripts

Hi Erel,

I am just getting to grips with the designer scripts which I find very useful, saves a lot of work!

But I was wondering if there is a specific use for the 'Variant Specific' scripts as I cant seem to see examples of it in use in either of the tutorials?

Thanks.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
The purpose of the "scaling" method described in the first post is to make it easier to cover all layouts with the generic script.

However in some cases you may need to add a variant, the standard landscape phone for example. The variant specific script lets you add code that will only be executed for this specific variant.

From my experience you should try to avoid adding more variants. Adding many variants will make it difficult to maintain and update the app.
 

CarlM

Member
Licensed User
Longtime User
OK thanks.
I must be getting layouts and variants confused. So I only need to add a Variant (of a layout) if for example I want to resize/add/change/hide controls when changing to a landscape view - in which case the 'specific variant' script overrides the generic one?
Thanks Erel.
 

moster67

Expert
Licensed User
Longtime User
Erel,

when we add views in the code, they are not available for the Designerscript since those views were/are not part of the original layout (I guess).

If I still want these added views to be considered by the Designerscript, I think I may perhaps use a panel as some kind of place-holder and then add my views to these panels. I haven't tried but I think it should work.

Alternatively, technically speaking, would it be possible to let us insert in the DesignerScript some "virtual/unassigned" views, then set the properties for instance "unassigned.button5.left=90%x". Then in the code, after adding the view, we assign the added view to the "unassigned" view and finally we run "RerunDesignerScript".

PseudoCode:

B4X:
sub Globals
        dim myCodeAddedButton as Button
end sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("1")

   myCodeAddedButton.Initialize("myCodeAddedButton")
   Activity.AddView(myCodeAddedButton,0,80dip,300dip,100dip) 'in this case the position is not really important
   unassigned.button5=myCodeAddedButton
   Activity.RerunDesignerScript("1",100%x,100%y)   
   
End Sub

Would it be feasible? :)


One last thing, already suggested, it would be nice if we could double-click on a view (or a group of selected vievs) in the abstract-designer so the line(s) were added automatically into the script.
 

corwin42

Expert
Licensed User
Longtime User
I think what you want to do is possible now with the magic size "-1" which means MATCH_PARENT or FILL_PARENT.

Just add a placeholder panel to your design. In the code you create your additional view and then add it to the placeholderpanel with:

B4X:
placeholderpanel.AddView(ManuallyAddedView, 0, 0, -1, -1)

If you then rerun the designerscript and the dimensions of your placeholder panel changes the manually added view will change its size, too.

I don't think that your approach with the unassigned views will give you any advantage.

The only feature that I'm missing currently in the designer scripts is the possibility to use something like process global variables in the scripts so you can calculate some scaling factors outside from the script and pass them to the script.
 

moster67

Expert
Licensed User
Longtime User
Yes, that would probably work, especially together with the magic size -1 you mentioned. This was more or less the first scenario I mentioned in my post.

However, I don't see if we use a panel how we can inherit all properties of the view we want to add to the panel. If I add by code for instance a button to a placeholder-panel, I can't set or modify the text-size of the button in the designer-script. If the "unassigned" view in my hypothesis scenario would be of the same type as the view to add, maybe we can also get all the related properties such as the text-size.

This means we can use this "unassigned" view in the scaling formulas proposed by Erel for adapting text-size on a tablet like:

B4X:
delta = ((100%x + 100%y) / (320dip + 480dip) - 1)
rate = 0.2 'value between 0 to 1. 
scale = 1 + rate * delta

unassigned.button5.TextSize = unassigned.button5.TextSize * scale

I don't think that would be possible by using a panel as a place-holder.

In either way, the panel-approach should work but an approach with unassigned views would be even better, I think. Of course, this might not even be technically possible.
 

corwin42

Expert
Licensed User
Longtime User
I see no advantage of adding a view by code when it is available in the designer. All views that are available in the designer I add there. Only views not available in the designer (AHDashboard, AHActionBar, AHViewpager etc.) should be added outside and for them I use a placeholder panel.
 

moster67

Expert
Licensed User
Longtime User
I see no advantage of adding a view by code when it is available in the designer. All views that are available in the designer I add there. Only views not available in the designer (AHDashboard, AHActionBar, AHViewpager etc.) should be added outside and for them I use a placeholder panel.

I agree with you that most stuff can be handled the way you suggested, especially using a panel. The placeholder-approach was something I started to think about this morning and then I just wrote down my ideas how it could be enhanced further:)
 

MiniDemonic

Member
Licensed User
Longtime User
I see no advantage of adding a view by code when it is available in the designer. All views that are available in the designer I add there. Only views not available in the designer (AHDashboard, AHActionBar, AHViewpager etc.) should be added outside and for them I use a placeholder panel.

How can you not see an advantage of adding views by code?
Adding views during runtime, like making a custom listview using scrollview and labels, adding multiple views like 10 labels and more, this is stuff I c/wouldn't do by hand.

Yes, that would probably work, especially together with the magic size -1 you mentioned. This was more or less the first scenario I mentioned in my post.

However, I don't see if we use a panel how we can inherit all properties of the view we want to add to the panel. If I add by code for instance a button to a placeholder-panel, I can't set or modify the text-size of the button in the designer-script. If the "unassigned" view in my hypothesis scenario would be of the same type as the view to add, maybe we can also get all the related properties such as the text-size.

This means we can use this "unassigned" view in the scaling formulas proposed by Erel for adapting text-size on a tablet like:

B4X:
delta = ((100%x + 100%y) / (320dip + 480dip) - 1)
rate = 0.2 'value between 0 to 1. 
scale = 1 + rate * delta

unassigned.button5.TextSize = unassigned.button5.TextSize * scale

I don't think that would be possible by using a panel as a place-holder.

In either way, the panel-approach should work but an approach with unassigned views would be even better, I think. Of course, this might not even be technically possible.

As for adding placeholder panels just to inherit the size, why?

for example:
B4X:
delta = ((100%x + 100%y) / (320dip + 480dip) - 1)
rate = 0.2 'value between 0 to 1. 
scale = 1 + rate * delta

button5.TextSize = button5.TextSize * scale
Works when adding views by code, you don't need to set their size inside the designer script, you can still set the size when adding the views.
If you know how to size the placeholder panel you know how to size your button.
 

Hubert Brandel

Active Member
Licensed User
Longtime User
it would be nice to have a IF ... THEN ... END IF in the script mode.
I want to scale the buttons and textcontrols up to a max size and
the rest only the listboxes.
 

Paulsche

Well-Known Member
Licensed User
Longtime User
B4X:
delta = ((100%x + 100%y) / (320dip + 480dip) - 1)
rate = 0.2 'value between 0 to 1. 
scale = 1 + rate * delta

I am very happy.
I have my app (18 layouts) with Designer Script
revised, with small adjustments, it works perfectly.
Previously had six layout variants, then only one.
 

Zeev Goldstein

Well-Known Member
Licensed User
Longtime User
hi
i am trying to use autoscale but it is not recognized
should i import or link any code or lib ?
i get it in red
 

Zeev Goldstein

Well-Known Member
Licensed User
Longtime User
ok - got it but it does not work
for wide screen it seems ok
but what about a screen height - i have a short physical screen and the bottom of my panel is hidden now
 
Status
Not open for further replies.
Top