Android Tutorial Supporting multiple screens - tips and best practices

Status
Not open for further replies.

Erel

Administrator
Staff member
Licensed User
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:

MiniDemonic

Member
Licensed User
A question:

Is it wrong to only use %y and %x instead of using dip?

For all of my applications, which aren't designed to run on tablets because I do not own one, I only use %x and %y when designing the layout, I create a new function that got all of the layout code and then call that in activity_create.

Once again, none of my apps are designed for tablets, because trying to do that without a tablet would be too much work.
 

moster67

Expert
Licensed User
Erel,

I find the designer-scripts marvellous. The only thing I do is to create a slightly different layout for landscape-layouts with larger devices. I am really a noob what regards scaling and so on but so far thanks to Designer-scripts I am very pleased.

In this regard: you wrote the following when using larger layouts:

- 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.

We treat the standard variant (320x480, scale=1.0) as the base variant. We then use this script code to calculate the scale factor:
B4X:
delta = ((100%x + 100%y) / (320dip + 480dip) - 1)
rate = 0.2 'value between 0 to 1. 
scale = 1 + rate * delta
I have been using the following way instead which you wrote somewhere here in the forum (although I don't find where now). Maybe it is more specific for font-size?:

B4X:
fontAdjust = ((100%x + 100%y) / (320dip + 480dip) - 1) * 3
lblMovies.TextSize=lblMovies.TextSize + fontadjust
Is there any particular difference?

Finally you wrote:

B4X:
Button2.SetLeftAndRight(100%x - Button2.Width * scale, 100%x)
How would I use the formula for instance at the left or somewhere in the middle like this:

B4X:
lblShortDesc.SetLeftAndRight(0%x, 20%x)

'or

myspinner.SetLeftAndRight(20%x,40%x)
Thanks!
 

Erel

Administrator
Staff member
Licensed User
I have been using the following way instead which you wrote somewhere here in the forum (although I don't find where now).
This was my first attempt of properly calculating a "good" scale factor. The one described in the tutorial is better as it correctly treats the current dimension size.


How would I use the formula for instance at the left or somewhere in the middle like this:
It depends on what you are trying to achieve. If you have 5 elements that should fit one after another then you should use percentage without applying the scale factor.
 

corwin42

Expert
Licensed User
- 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.

We treat the standard variant (320x480, scale=1.0) as the base variant. We then use this script code to calculate the scale factor:
B4X:
delta = ((100%x + 100%y) / (320dip + 480dip) - 1)
rate = 0.2 'value between 0 to 1. 
scale = 1 + rate * delta
I use this method for scaling my views and it works great for Activity.LoadLayout().

The problem is that I have a few panels where I use Panel.LoadLayout(). Here %x and %y are not the device dimensions but the panel dimensions so the delta is not calculated correctly. Is there any chance to fix this?

Perhaps some special characters like 100%dx and 100%dy can be added for device dimensions?

Another great addition would be the ability to access process global variables from within designer scripts. So it would be possible to calculate the scale factor and store it in a global variable and all scripts could access it without making the calculation every time.
 

corwin42

Expert
Licensed User
The problem is that in my program the panel size is variable and so I have to adjust it from outside the script. I think there is currently no perfect solution for this.

Gesendet von meinem LG-P500 mit Tapatalk 2
 

Dominex

Active Member
Licensed User
With this function you can get the scale of the display. For now, it worked fine with all devices that I've got to try.
B4X:
Sub Activity_Create(FirstTime As Boolean)
   Msgbox(DisplayScala,"")
End Sub

Sub DisplayScala As Float
   Dim c As Canvas
   c.Initialize(Activity)
   Return Round((c.MeasureStringHeight("X,_", Typeface.SANS_SERIF, 10)/10+0.15)/0.25)*0.25
End Sub
 

Dominex

Active Member
Licensed User
This code is not related. You will not be able to use it with the designer scripts.
Is true, and frankly I wonder why did not I think of it before because there is an easier way to get the scale of a display.
B4X:
Sub Scala As Float
   Return 100dip/100
End Sub
Used in the designer so I think.

B4X:
label1.TextSize = label1.TextSize * (100dip/100)
;)
 

jaminben

Member
Licensed User
Hi,

I've been reading this tutorial to try and work out the best way to set some text labels to the far right of any screen no matter what size the screen is and come up with the below:

B4X:
Sub setLabelTextRight(TextString As String, TextType As Typeface, TextSize As Int, RightMargin As Int)

   Dim Canvas As Canvas
   Dim Result As Int
            
   Canvas.Initialize(Activity)
   Result = ((Activity.Width - RightMargin) - Canvas.MeasureStringWidth(TextString, TextType, TextSize))

   Return Result

End Sub
And then used like this:

B4X:
lbl_SomeText.Left = setLabelTextRight(lbl_SomeText, Typeface.DEFAULT, 14, 10)
This seems to work ok but I'm wondering how I can apply the dip to the end result?

Any ideas? or am I going about it in the wrong manner?

Thanks
 
Last edited:

jaminben

Member
Licensed User
Why not use the following designer script:
B4X:
lbl_SomeText.Right = 100%x
Set the label's horizontal alignment to RIGHT.
I need to set it at after something else has occured... so it starts on the left then it gets moved to the right once its completed its task. So the designer script wont work for me will it?

If the designer script wont work can I point you back to the original question of how to assign dip to the end result of the below code?

Thanks

Ben
 

JonPM

Well-Known Member
Licensed User
I need to set it at after something else has occured... so it starts on the left then it gets moved to the right once its completed its task. So the designer script wont work for me will it?
You can use Activity.RerunDesignerScript in your code as needed.
 

jaminben

Member
Licensed User
You can use Activity.RerunDesignerScript in your code as needed.
Ah, thats pretty cool.... but it would set it back to its starting position (left) which is the default designerscript layout. I guess I could set my starting position in the main code then Rerun.RerunDesignerScript to set it right but that kinda seems backwards.

I'd still like to know if theirs a way to set dip to the result from my above code... if its possible or if dip is required.

Thanks

Ben
 

salmander

Active Member
Licensed User
Hi erel how can I resize the font size based on text length so that it all fits in views width. help please.
 
Status
Not open for further replies.
Top