Android Question Webview, pagedown with WebviewExtras

bryon

Member
Licensed User
Longtime User
In my app I'm pulling 25 records at a time from a DB and building a page to display in Webview. Scrolling down is not a problem but scrolling up requires loading the 25 records and then programatically scrolling to the bottom of the page for seamless viewing.

I'm using WebviewExtras library for the scroll (and have also tried WebveiwExtended library) and after the records are rendered, the scroll seems to get skipped over, or it happens and then immediately jumps to the top of the webview again.

In the test below, the scroll happens exactly every other time.

Does anyone know what's going on here? @warwound ?

B4X:
#Region  Project Attributes
    #ApplicationLabel: B4A Example
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
Private web As WebView
Private wvx As WebViewExtras
Private HTML As String


End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")
web.Initialize("web")
Activity.AddView(web,0,0,100%x,100%y)
HTML=File.ReadString(File.DirAssets,"latin-lipsum.txt")

Render
End Sub

Sub Activity_Resume
    Dim t As Int
    Render
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Render()
    web.LoadHtml("")
    Dim strg As String
    For t= 0 To 10
        strg=strg & HTML
    Next
web.LoadHtml(strg)
Log(wvx.GetContentHeight(web))
Log(wvx.pageDown(web,True))

End Sub

And here is the log:

** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
81944
true
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
81944
false
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
81944
true
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
81944
false
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
81944
true
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
81944
false

Any suggestions are appreciated!

-Bryon
 

warwound

Expert
Licensed User
Longtime User
Is it a timing issue?
You call the LoadHtml method then call PageDown but the WebView hasn't finished rendering the HTML when PageDown is callled?
 
Upvote 0

bryon

Member
Licensed User
Longtime User
Thanks warwound. I considered that, but unlike LoadUrl, LoadHtml shouldn't (?) see the loading times with a string as opposed to a url. Also, there is no PageFinished event with LoadHtml so I'm not sure how I would wait for the page other than a software delay (yuck).
 
Upvote 0

bryon

Member
Licensed User
Longtime User
Adding the Do/While works but seems a little 'brute force'...


B4X:
#Region  Project Attributes
    #ApplicationLabel: B4A Example
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
Private web As WebView
Private wvx As WebViewExtras
Private HTML As String


End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")
web.Initialize("web")
Activity.AddView(web,0,0,100%x,100%y)
HTML=File.ReadString(File.DirAssets,"latin-lipsum.txt")
'HTML="test"
Render
End Sub

Sub Activity_Resume
    Dim t As Int
    Render
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Render()
    web.LoadHtml("")
    Dim strg As String
    For t= 0 To 1
        strg=strg & HTML
    Next
web.LoadHtml(strg)

    Do While wvx.pageDown(web,True) = False
        DoEvents
    Loop
End Sub
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
It's not the 'loading time' but the 'rendering time' - the WebView will take a noticeable amount of time to render any amount of HTML.

A more elegant solution would be for you to add some javascript to your HTML:
  • Add an 'onload' callback to the HTML <body> element:
    B4X:
    <body onload="B4A.CallSub('PageRendered', true)">
    Once the HTML has been rendered, your b4a sub named 'PageRendered' will be called.
  • Create this 'PageRendered' sub and in it call the PageDown method:
    B4X:
    Sub PageRendered
        wvx.pageDown(web,True)
    End Sub
 
Upvote 0

bryon

Member
Licensed User
Longtime User
...except now, it seems the view is redrawn after the pagedown and it immediately goes to the top again. Do you know of a way I can determine when the the view is finished redrawing?

I don't know much about the reflector library but it would seem this may be a start.
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
Sounds like another timing issue.
You're calling PageDown too soon...

Can you start a Timer in PageRendered sub and call PageDown in the Timer Tick event?
Try an interval of 4000ms or more.
See if calling PageDown after a delay works as desired.
 
Upvote 0

bryon

Member
Licensed User
Longtime User
That is working now. It doesn't take much, I've set the interval at 50ms and it's working consistently.

Thanks again, Martin!

-Bryon
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
A more robust solution might be something like this:
B4X:
Sub PageRendered
    CallSubDelayed(Me, "DelayedPageDown")
End Sub

Sub DelayedPageDown
	wvx.pageDown(web,True)
End Sub

CallSubDelayed (Component As Object, Sub As String)
CallSubDelayed is a combination of StartActivity, StartService and CallSub.
Unlike CallSub which only works with currently running components, CallSubDelayed will first start the target component if needed.
CallSubDelayed can also be used to call subs in the current module. Instead of calling these subs directly, a message will be sent to the message queue.
The sub will be called when the message is processed. This is useful in cases where you want to do something "right after" the current sub (usually related to UI events).
Note that if you call an Activity while the whole application is in the background (no visible activities), the sub will be executed once the target activity is resumed.

https://www.b4x.com/android/forum/t...teract-between-activities-and-services.18691/

This assumes that by the time CallSubDelayed has called DelayedPageDown, your WebView will have re-rendered itself.
That may or may not be the case - you'll have to do some through tests.
But if that is the case then this solution will be better than a Timer delay.
 
Upvote 0

bryon

Member
Licensed User
Longtime User
I just tried this and while the idea is sound, in practice it behaved the same as it did before your timer solution. It seems, somehow, that the pagedown is getting in there before the View re-draw. Ideally, I'd like the view to provide a 'RedrawFinished' event.
 
Upvote 0
Top