javascript - Android webview wont consistently open to last-scroll location -
i have android app uses kitkat-level webview display documents (primarily e-reader consumes html files). when user opens document within app (and webview newly instantiated document) want scroll last known cursor location within document, user can pick reading left off. retain , persist cursor location indepedently of webview allow such scrolling when first open document.
problem: when document first opens, can see initial highlighting of last known location (cursor) in javascript document, in perhaps 50% of cases i've seen document somehow scrolls top , cursor location no longer shown. how , why scrolls top in these cases mystery me (i dont believe programatically make scroll top in way).
notes: achieve scrolling within webview i'm using javascript function invoked webview fragment (via webview.loadurl) once webview has signalled has loaded , ready use. callback document view fragment webview:
@targetapi(build.version_codes.kitkat) @suppresslint("clickableviewaccessibility") @override public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) { view mainview = inflater.inflate(r.layout.document_webview_reader, container, false); getreaderactivity().setfooterlocationbarvalues(mainview); mwebviewloading = true; final webview webview = (webview) mainview.findviewbyid(r.id.document_webreader_webview); webview.getsettings().setjavascriptenabled(true); webview.getsettings().setallowfileaccessfromfileurls(true); webview.getsettings().setallowuniversalaccessfromfileurls(true); webview.setscrollbarstyle(view.scrollbars_inside_overlay); webview.setbackgroundcolor(0); webview.setbackgroundcolor(color.ltgray); if(build.version.sdk_int >= build.version_codes.kitkat) { webview.setwebcontentsdebuggingenabled(true); } mjsinterface = new jscall(); webview.addjavascriptinterface(mjsinterface, mjsinterface.getinterfacename()); webview.setwebviewclient(new webviewclient() { public void onpagefinished(webview view, string url) { drawinitialcursor(getdocument().getcursorrange()); } }); private void drawinitialcursor(final wordrange range) { if( getreaderactivity().activityispaused() ) return; getactivity().runonuithread(new runnable() { @override public void run() { log.d(readerapplication.vdreader_tag,"highlighting initial range"); wordrange currentfragmentrange = mtextfragmentranges.get(mcurrenttextfragmentindex); int relativelocation = range.getlocation() - currentfragmentrange.getstartrange(); wordrange rangeincurrentfragment = new wordrange(relativelocation, range.getlength()); webview webview = (webview) getactivity().findviewbyid(r.id.document_webreader_webview); string applystring = string.format("javascript:highlightinitialrange(%d,%d,%d,%d)", rangeincurrentfragment.getstartrange(),rangeincurrentfragment.getendrange(), mcurrenttextfragmentindex, mcursorpositionsetting.ordinal()); webview.loadurl(applystring); } }); }
the javascript 'highlightinitialrange' is:
function highlightinitialrange(start, end, elementindex, cursorpagepositionsetting) { if( wordhighlightapplier == null ) { wordhighlightapplier = rangy.createclassapplier("voice-dream-spoken-word", { elementtagname: "vdword"} ); } if( selrange == null ) { selrange = rangy.createrange(); } wordhighlightapplier.undotorange(selrange); var mynodelist = document.queryselectorall(vd_cssselector); selrange.selectcharacters(mynodelist[elementindex], start, end); wordhighlightapplier.applytorange(selrange); var wordcursor = document.getelementsbytagname("vdword"); wordcursor[0].scrollintoview(true); }
the above javascript finds relevant range in document (as combination of element index , offset within element), applies highlight (via rangy) , scrolls highlighted element view.
as know strategy works since can see highlighting correct location when document loaded , webview ready use; in ~50% of times run this, see cursor highlight briefly , disappears document scrolled top (assuming cursor range beyond initial page of document of course).
** post image of correct cursor highlight here apparently i'm not reputable enough allow such triviality. **
question: missing webview document rendering here? know working correctly in terms of highlighting , initial scroll-into-view highlighted element, , i'm executing highlighting code once webview loaded (notified via onpagefinished on webview client). there within webview non-deterministically executing when document loaded ensure document displayed @ top-most scroll position?
*** resolved owner
in hindsight resolution problem obvious: using 'wrapper' html document in loaded various js libraries - jsquery, rangy etc, , within using jsquery replace document body 'real' html document want load/read. html document point of view loading occurring in 2 phases:
- load wrapper
- wrapper loads real html document via $("body").load("")
because of two-phase loading, app seeing webview.onpagefinished event being fired - signalled loading of wrapper html, not full body. needed add explicit call webview / javascript app once jsquery .load method had completed successfully:
<body> <script> $( "body" ).load( "%s", function( response, status, xhr ) { if (status == "error") { var msg = "sorry there error: "; $("#error").html(msg + xhr.status + " " + xhr.statustext); } else { // append our line highlight div, floating var newdiv = $("<div id='vd-spoken-line' class='voice-dream-spoken-line'/>"); $( "body" ).append(newdiv); notifydocumentbodyloaded(); <-- new callback android app here } }); </script> </body>
so 'notifydocumentbodyloaded' executes android/java code formerly executed in onpagefinished method, after true document body has been loaded jsquery. way sequence of loading -> cursor drawing happens in correct order. solved.
as mentioned in updated question (adding answer can resolve this):
in hindsight resolution problem obvious: using 'wrapper' html document in loaded various js libraries - jsquery, rangy etc, , within using jsquery replace document body 'real' html document want load/read. html document point of view loading occurring in 2 phases:
load wrapper wrapper loads real html document via $("body").load("") because of two-phase loading, app seeing webview.onpagefinished event being fired - signalled loading of wrapper html, not full body. needed add explicit call webview / javascript app once jsquery .load method had completed successfully.
Comments
Post a Comment