Android Tutorial Edge Case use for B4XPages: Changing Orientation for Many Pages

This is for advanced users only. Please see Erel's important cautions about using this approach below.

This post and examples demonstrate how to extend B4XPages to allow orientation changes. It should be considered a beta version, since you may encounter some problem that I haven't yet discovered.

Background: B4X continues to improve its cross-platform support. With the advent of B4XPages, you can share almost all of your code across platforms and/or develop single platform apps with a simpler lifecycle. As a design feature of B4XPages, there is a limitation that requires you to be locked to one orientation. There are some good rationales for this such as keeping the app lifecycle simple and trying to keep with some Android platform recommendations. This is probably a best practice for most people.

As for me, my apps support both / changing orientation. My philosophy is that it is better to enable features that a signification percentage of users desire or will be frustrated by the lack of the feature. In my experience, this condition exists for many users of my apps (they want to be able to change orientations). I try to reduce friction for my users where I can.

I have also written a custom class that is used more for porting B4A apps to B4i. It replicates many of the same features of the Android Activity on iOS. It may be easier in some cases to use the ActivityClass if you have a large existing Android app that you need to have the ability to change orientations. If you are developing a new app or have an existing iOS app, it may be easier to use the procedures I outline below. Note: if you are OK with being tied to one orientation, you should not use this technique. It is a little more complicated to manage the code than it is for a standard B4XPages project.

How To Use:

1. Required Libraries


Your Android project must include the IME and JavaObject libraries. These libraries are used for catching the HeightChanged event. There is also a manifest line that prevents the activity from being destroyed when the orientation changes.

2. You must set #FullScreen: False

This is in the main module on Android. This is because the IME library does not work correctly otherwise.

3. Adding a new page.

You can add a new page just like you normally would. Remove the Initialize and B4XPage_Created subs. These will be replaced in the code below. Paste the following code into the new page after removing those two subs.

B4X:
#Region PageEvents
'-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
'It is recommended that you not modify the code
'below under most circumstances.
'-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Public Sub Initialize
    Return Me
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
#if not(b4j)
    LogColor($"B4XPage_Created"$, Colors.Green)
#end if
    FirstTime=True
    Root = Root1
#if b4a
    LastOrientationPortrait=(Main.ActivityHeight > Main.ActivityWidth)
    B4XPage_Resize(Main.ActivityWidth, Main.ActivityHeight)
#end if
End Sub


Private Sub B4XPage_Resize(Width As Int, Height As Int)
#if not(b4j)
    LogColor($"B4XPage_Resize(Width=${Width}, Height=${Height})"$, Colors.Green)
#end if
    Dim AlreadyRestored As Boolean
    If LastOrientationPortrait=(Width>Height) Or Root.Width<>Width Or Root.Height<>Height Or FirstTime Then
        If Not(FirstTime) Then UI_Save_State
        UI_Create(Width, Height)
        If Not(FirstTime) Then
            UI_Restore_State
            AlreadyRestored=True
        End If
    End If
    If Not(FirstTime) And Not(AlreadyRestored) Then
        'save state
        UI_Save_State
        'restore state
        UI_Restore_State
    End If
    LastOrientationPortrait=(Height>Width)
    FirstTime = False
End Sub

Sub B4XPage_Disappear
#if not(b4j)
    LogColor($"B4XPage_Disappear"$, Colors.Green)
#end if
    'save state
    UI_Save_State
End Sub

#if b4a
Sub B4XPage_Appear
    LogColor($"B4XPage_Appear"$, Colors.Yellow)
    If LastOrientationPortrait=(Main.ActivityWidth > Main.ActivityHeight) Then B4XPage_Resize(Main.ActivityWidth, Main.ActivityHeight)
End Sub
#end if

'-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
'Feel free to edit the code below.
'-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
#End Region


Sub UI_Create(Width As Int, Height As Int)
#if not(b4j)
    LogColor($"UI_Create Width=${Width}, Height=${Height}"$, Colors.Magenta)
#end if
    Root.Width=Width
    Root.Height=Height
    Root.RemoveAllViews
'You will generally want to keep the 3 lines above and edit below.
    Root.LoadLayout("Page2")
End Sub

Sub UI_Save_State
#if not(b4j)
    LogColor($"Saving State"$, Colors.Magenta)
#end if
    ToSave_Text=B4XFloatTextField1.Text
End Sub

Sub UI_Restore_State
#if not(b4j)
    LogColor($"Restoring State"$, Colors.Magenta)
#end if
    B4XFloatTextField1.Text=ToSave_Text
End Sub

The region PageEvents contains the code that manages changes to orientation and calling the three important subs at the end. You will generally not want to change that region.

Add the following to Class_Globals. This is used for tracking whether the page is being created for the first time. It also contains LastOrientationPortrait, which is self-explanatory.

B4X:
Private FirstTime As Boolean = True
Private LastOrientationPortrait As Boolean

4. UI Events

The three subs at the end are where you will be working to start. Below that, you can add any other code as you normally would.

UI_Create - This is where you create your UI. It is best to use this sub for creating your UI, because it will have accurate dimensions on iOS. This sub is called when the page is first created and when you change orientation (or any circumstance where the size is changed -- like resizing the window in B4J).

UI_Save_State - This is where you will want to save the state of your UI (for example any UI changes that have happened after it was first created). Save these values to global variables.

UI_Restore_State - This is where you restore changes to your UI. In the example above, it restores the text to a B4XFloatTextField. That way if the app changes orientation, it can restore any changes that the user has made.

That's it! I have tried to keep it pretty simple, and I think it is simpler than the typical Activity lifecycle on Android. Please let me know if you encounter any issues or have recommendations. I hope you find it useful.
 

Attachments

  • ezgif.com-gif-maker.png
    ezgif.com-gif-maker.png
    244.4 KB · Views: 711
  • b4xpages_orientation_example.zip
    203.2 KB · Views: 587
Last edited:

Sandman

Expert
Licensed User
Longtime User
This is very interesting, but as I haven't really started with B4XPages yet I can't understand the consequences of doing things like this. I would be a little hesitant to use it until I heard Erel comment on it. Any chance for a comment, @Erel?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Any chance for a comment, @Erel?
Any solution is good if it helps the developer gets his or her job done.

There is no magic. You will gain support for orientation changes but it will make other things more complicated.
Believe me that I'm aware of the possibility of using configChanges to prevent the activity from being destroyed and chose not to use it with B4XPages for good reasons :)

but as I haven't really started with B4XPages
Once you start using it you will see how simple things are when you don't need to worry about the UI state and that you can treat other pages and their views as regular classes and objects with no special life cycle.

Bottom line: most users should use the default B4XPages. If there is any specific "page" that needs to be displayed in other orientation or support multiple orientations, then you can use an additional activity outside of B4XPages.
If the whole app should support multiple orientations and you want to build a cross platform solution then this solution can be a good solution.

To avoid confusing other members when posting questions about this implementation, please don't call it B4XPages. This is not B4XPages. It breaks many of the fundamental assumptions behind B4XPages.
 
Last edited:

Jack Cole

Well-Known Member
Licensed User
Longtime User
Bottom line: most users should use the default B4XPages. If there is any specific "page" that needs to be displayed in other orientation or support multiple orientations, then you can use an additional activity outside of B4XPages.
If the whole app should support multiple orientations and you want to build a cross platform solution then this solution can be a good solution.

I have added a link to your cautions in the OP. This solution is only for those who know they really need it and are willing to deal with the the increased code complexity.
 

Robert Valentino

Well-Known Member
Licensed User
Longtime User
This is very interesting.

I've been rewriting an old app in B4XPages and have been allowing screen rotation (assuming / hopefully someday B4XPages will support it).

But I haven't noticed any problems with rotating the screen. Where do the problem come in?

BobVal
 

Robert Valentino

Well-Known Member
Licensed User
Longtime User
Appears to. It is like the app restarts. I only have 1 page that goes gown another level. So the app restarting just brings me back to main page. Where normal rotation just stays in the activity. I can live with the app resetting. Assuming it isn't hurting something I'm not seeing.
 

AnandGupta

Expert
Licensed User
Longtime User
If you only need one page and you don't mind if the app resets, you should be fine using the standard approach.
Agree.
B4XPages have one and only one activity, so it gets rotated and activity_create executed.

For only one page it is not beneficial. I have two pages activity, main and webview and use the standard / activity logic.
B4XPages gives benefit when you have many activity with many classes and you want to call/use variables from other activities. As there is not activity related complexity then.

Regards,

Anand
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
1. There are many advantages to using B4XPages with a single page. The number of pages is not relevant. Full list here: https://www.b4x.com/android/forum/threads/b4x-b4xpages-what-exactly-does-it-solve.119078/#content

2. Don't use B4XPages without locking the orientation of the relevant activity. All kinds of things will break.

3. @Jack Cole I recommend giving this implementation a different name. Questions like this: getPreviewSize error together with Orientation Change on B4XPages will confuse members who are new to B4XPages and are not familiar with the small details.
 

AnandGupta

Expert
Licensed User
Longtime User
1. There are many advantages to using B4XPages with a single page. The number of pages is not relevant. Full list here: https://www.b4x.com/android/forum/threads/b4x-b4xpages-what-exactly-does-it-solve.119078/#content
Agree the benefits, but I need webview which should be rotated as second activity, so B4XPages does not help much in my specific case app with the one main activity, which has menu only.

Obviously I will take benefit when I make a complex app.

Regards,

Anand
 

Robert Valentino

Well-Known Member
Licensed User
Longtime User
When I said 1 page I meant that most of my pages only go 1 deep

Evey Icon is a page and only 2 of the icons goes down 2 pages (Events goes to a list of events and then individual event same type of thing with players).
 

Attachments

  • Screenshot_2020-11-04-15-26-18.png
    Screenshot_2020-11-04-15-26-18.png
    227.7 KB · Views: 337

Jack Cole

Well-Known Member
Licensed User
Longtime User
When I said 1 page I meant that most of my pages only go 1 deep

Evey Icon is a page and only 2 of the icons goes down 2 pages (Events goes to a list of events and then individual event same type of thing with players).

Seems like that could be a problem for your users. I would probably lock it in a single orientation or use my procedure above.

Your method would reload the app and kick them back to the main page if they change orientation.
 
Top