I'm developing an application that requires somewhat complex html rendering (basically text and images, but the text may have some advanced features like custom font and path) and I'm using webviews to render the content.
The problem is that some of the pages doesn't render text in some devices.
I'm using all three callbacks:
onPageFinished;
onProgressChanged;
onNewPicture (deprecated, but some users related that it works)
but even if they're triggered I get a WebView that is missing the text in most of the pages. On some devices when I touch the webview the text appears, but it's an erratic behaviour that can't be trusted.
I also tried called invalidate() constantly but it doesn't force the text to draw. I'm logging the webview console and apparently it's not throwing any error (notice that sometimes the text is rendered perfectly and the problem occurs for pages that rendered correctly before). I'm configuring my webview like (I know that some methods are deprecated, but some other solutions related that it worked for then):
offScreenWebView.layout(0, 0, viewPortWidth, viewPortHeight);
offScreenWebView.getSettings().setUseWideViewPort(true);
offScreenWebView.getSettings().setLoadWithOverviewMode(true);
offScreenWebView.getSettings().setTextZoom(100);
offScreenWebView.setDrawingCacheEnabled(true);
offScreenWebView.getSettings().setJavaScriptEnabled(true);
offScreenWebView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
offScreenWebView.getSettings().setPluginState(WebSettings.PluginState.ON_DEMAND);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
offScreenWebView.enableSlowWholeDocumentDraw();
}
I'm at loose now, because I've no clue why the text doesn't render and it seems I can't force it to render.
I've managed to somewhat solve my problem. The main issue I was having was due to the webviews were being loaded simultaneously (they're used at a viewpager) and probably this incurred in some concurrency problem when the webviews were loading the font. I encapsulated the URL loading and ensured that the webviews were loaded one page at a time.
Also, when destroying the webviews I couldn't call any method like cancelTimer, this resulted in problems when rendering other pages with similar font. The only thing I did was to point the references to null.
It's somewhat slow but at least it was the only way to get consistent rendering results using the webviews.
This is probably due to the latest update to Android System Web View, that comes automatically from Google Play. If you uninstall the update, it helps. It is of course not a really good solution((((
Another workaround is disable HW accelleration in the webview
Related
I have a WebView load html data from a response in a ViewPager which contain WebView,so if I swipe fast the data will load but some portion will be white, initial i thought its a render issue onRenderProcessGone but it was not coming it to that method
#Override
public boolean onRenderProcessGone(WebView view,
RenderProcessGoneDetail detail) {
}
This is how the render webview in view pager looks like
if you see the above image you can see the some portion not rendered. if I swipe back after going to other page, the data will load perfectly, so how can I solve the issue. i followed the Managing WebView objects
but noting is working, Hope some one will help to fix the issue
This was a bug in chrome version v74, when we using webview inside cardview, as i can see your webview is inside cardview,So dont worry they have done a fix in canary version v75 for details check the link below
Rendering issue
So for testing you can download the canary version of chrome from play-store and enable it as your webview from developer console, after that check whether you are getting the issue or not.
If fixed wait for the stable version of chrome v75 meanwhile if you need a urgent fix , i can suggest you that change the cardview to linear-layout and make card image with shadow as background,for generating card you can check shadow4android
Else wait for the stable relase,Hope this helped you
I have a WebView I'm loading in an activity in order to have it preloaded so that it pops up immediately in a different Activity (launched from the first).
The problem is that in order to instantiate a WebView, I have to pass in a Context, in this case it's the first mentioned above.
So it works great, and the second Activity shows the WebView just fine. The problem is that if I click a <select> dropdown in the WebView, its selector dialog shows up UNDER the WebView. It feels like the select doesn't work at all until you hit the back button and briefly see the selection dialog just before you return to the parent activity.
It seems as though when I append the WebView to the layout in the second activity, it's modals get attached to that activity's window, but the WebView itself is attached to the parent activity's window, so it shows in a higher point in the hierarchy.
How can I possibly change the Context of the WebView after it's been instantiated?
This is a very difficult problem to solve -- I have to create the WebViews before the activity is started, but I also need the selection dialogs to work.
Please if anyone can give me some insights here I'd greatly appreciate it.
This is for an SDK project, so I will not have access to the parent activity. Also, saveState isn't working, because the bulk of what is shown in the WebView is generated by JavaScript, and the full DOM stack doesn't transfer.
You can try to create the WebView with a MutableContextWrapper:
MutableContextWrapper mMutableContext=new MutableContextWrapper(context);
WebView mWebView=new WebView(mMutableContext);
and later on you could do
mMutableContext.setBaseContext(newcontext);
But ...
WebView is a very complex component that will probably be using the passed context to create other objects like Handlers. WebView probably uses those handlers to post stuff to the original UI thread, so at the end you'll probably have a View with a mix of contexts, you know, a double memory leak (if it ever works properly)
Webview spans at least 1 thread "webcore" that is where the action happens and is also in constant communication with the original UI thread with ... handlers? through the original context? who knows!
There are even 2 different webview engines: Kitkat is chromium-based while jelly bean and previous versions use AOSP/WebView. So you have an additional breaking point.
The reasons you state are not strong enough imho. WebView is not that slow. If the app you load is, try to optimize it. There are a lot of things you can do for that, like loading the HTML & graphics from internal assets.
In my App (it's browser) I have the same problem. I don't like to load WebView every time when user back to App. And I've solved this problem partially. I've overridden onBackPressed() on my HomeActivity and use moveTaskToBack(true) instead of super.onBackPressed(). So when user use system back on HomeActivity it does't destroy Activity and all views. It just minimize the App. Visually it's the same behavior but if user try to run App by launch icon, all views already loaded. I know it's temporary solution and all views can be destroyed by system any time but it gives quite good result. And covers a lot of cases for me.
I'm developing a mobile app in jQueryMobile and PhoneGap. Often, due to the nature of jQM or because it's about loading data, a page will need some last-minute adjustments before it is shown. For example, form fields need to be filled in with dynamically retrieved data, or the contents of list items need to be given a slightly different style in order to fit better.
I am currently doing all these adjustments using the pageBeforeShow event handler. But I wonder if I shouldn't have been using the pageBeforeChange event handler. The jQM docs do not really make it clear how these two events relate to each other, i.e. which is fired first.
What I know
I do know the following
pageBeforeShow and pageShow are triggered after all of jQM's markup (e.g. making list items look pretty) has been applied.
pageBeforeShow and pageShow are bound to a specific page, whereas pageChange is called whenever a change of page occurs (so if you want to make specifi changes to one page before the user gets there, you need to test the event.toPage property)
Why I'm asking
And this is the background why I want to know if page(Before)Change is a better candidate.
jQuery Mobile page transitions are awkward on many devices. The big issue on Android devices is that page changes are jumpy: regardless of the transition type (fade, pop, etc), the page being left by the user will pop back into view briefly after the new page has more or less finished in the browser.
In my experience, this occurs mostly when other animations are running or are started while the page transition takes place. Basically, the Android browser doesn't seem to want to apply transitions to elements that are not actually in view, and it will flip back and forth between jQM pages as a result.
I've already developed a workaround where I delay any markup and form adjustments for a page by about 1000ms, which prevents the flashing but does mean that the user may be seeing these adjustments happen on screen after the page has come into view.
This analysis makes me think that pageBeforeChange might be a better candidate for attaching any markup and form adjustments. But it will be a big rewrite of the code, and I don't know what unforeseen stuff I will be getting into. Anyone have any experience with these events?
I'm not sure pagebeforechange would be the best place to do this. I don't see pagebeforechange as a page-level event, but more of a site-level event. I use it mainly if I want to take over navigation or to build dynamic pages.
I use pageinit when I want to attach event handlers to a page. I use pagebeforeshow when I want to change the contents or look of a page before it is shown.
Hope this helps.
When you have some clickable content like <a>, <input> or <area> and before this you have an absolutely positioned element with a bigger z-index, there is the wrong behavior of 'click-through'.
I click on the area where the clickable element is behind the front element. In other browsers there is the right behavior that the click does not go through the front element. But only in Android Browser you can click through the front element and activate the element behind. This is a known bug and you cannot avoid it. It's even in newer versions (I test on 2.3.3 in the official Android emulator).
There are some workarounds described in some forums but none of them worked for me.
I tried to put an <iframe> or an <a> between front and back
I tried to change the DOM so maybe the browsers state is refreshed
I tried to have the back elements be positioned as well
None worked
I'm especially having problems with the image map's area elements.
Has anyone had the same issue and managed to work around it?
I'm specifically interested in solutions which are tested against image maps.
I am wondering about a few things here. First, what is the purpose of having an overlaid image and using the image maps? I see you're including jQuery - can you use the hover event with jQuery to change the orientation of the images and do the swap? What about attaching to the click event for the image map, and checking to see if the lightbox is open. If it is, then return false;.
Just trying to think out loud. Sometimes another take on it can be helpful.
This is a quick blindfolded reply, so let me know if I should expand/fix it further. The general idea being a CSS class for both the hover and focus events that disables pointer interaction.
yourElementClass:focus, yourElementClass:hover {
pointer-events: none;
}
Actually I've managed to avoid it by moving the objects below to let them be not visible.
But in cases similar to yours the only workaround that actually works is to manage all the clicks in jquery (especially the ones on the background) and to bind/unbind the clicks events on needs.
there are also some things that could help on some version/mobiles (but do not solve the problem)
the above item has background:rgba(0,0,0,0.1);
you should put a gif or png as background of the above element (as well as the background color as point 1)
using thouchstart instead of click as bind event sometimes helps.
the actual version of android/browser are not affected with this bug (or at least it never happen to me) but it could be nice to know the affected versions. If someone has a list.
Is it possible to listen for WebView page load full completed and then capture screenshot of loaded page?
I tried two ways, but they both not working:
using WebViewClient and onPageFinished(WebView view, String url).
It doesn't work (and it described in docs) because Picture may be not ready it this moment
using WebView.PictureListener and onNewPicture(WebView view, Picture picture)
It also doesn't solve problem, because this method calls undetermined times. At first time, picture often contains only part of page content. And I don't know way how determine, that the current call is last for the page.
I want to notice, that the problem is to find right moment to make screenshot, and not how it can be done.
[Note that since I answered this, onNewPicture has been deprecated (see http://developer.android.com/reference/android/webkit/WebView.PictureListener.html and What does "This method is deprecated" mean for application developers). Unfortunately, there is no information on what replaces it, or at what API levels it was supported. I guess that means that you use this at your own risk.]
I think you’re correct that onNewPicture is the right place to capture a screenshot of a loaded WeView page, but also correct that it is hard to know when to actually do the capture.
It appears that WebView calls onNewPicture whenever there has been any drawing. For example, it calls onNewPicture repeatedly when the search bar is in keyboard entry mode and the cursor is flicking. Similarly, for some web pages (eg www.yelp.com/nyc) it calls onNewPicture repeatedly, even after the page has finished drawing, probably because of the flashing cursor in the Search box. But at the other extreme it will call onNewPicture only once (eg if the user drops down an iGoogle item).
So there’s no easy rule? The approach we’ve taken is to
monitor a range of events that are involved / affect page loading – such as shouldOverrideUrl, onPageFinished, focus changes, scrolling start/end, in addition to onNewPicture
run a timer (2secs is working well) on onNewPicture, reset by a new onNewPicture
implement a page loading FSM which uses the events and timer expiry as inputs, and moves through a series of state/action transitions, to the point where it decides it genuinely has a new picture.
Not pretty, but it works, with very few cases where it captures the same picture twice – and no cases where it fails to capture a picture where it should.