My understanding is that WebView's PictureListener.onNewPicture() is called whenever the already loaded page finished rendering (or re-rendering in case certain events make the WebKit engine re-calculate page layout).
I also (think that I) understand that those WebView renders are controlled internally in WebView and ordinarily I wouldn't care how they are being generated.
However, for a certain function in my app, I need to trigger an extra PictureListener.onNewPicture() without re-loading the url (i.e. no reload() or loadUrl()).
Essentially, what I am looking for is a function like repaint() or redraw() or some other mechanism that only generate one more PictureListener.onNewPicture() and that's it (without the slowness and overhead associated with re-loading data).
Is there a way to achieve this?
Try using the requestLayout() function.
not sure if it will do what you want, but it worth a try.
Related
I have a webview which loads content from URL. However, the webview reloads the content on screen rotation, which I would like to avoid.
I have checked similar questions on SO which all recommended either:
Putting orientation into android:configChanges="orientation..." which is generally discouraged and I don't want to use it
Or saving the webview's state with webView.saveState(outState)
As for the 2nd option, the documentation now states:
Please note that this method no longer stores the display data for this WebView. The previous behavior could potentially leak files if restoreState(Bundle) was never called.
Which means that it's not the solution either. Is it possible to solve this?
My answer to this question was just accepted but I started to wonder when exactly one needs to invalidate() a View and when it is not necessary?
After a bit of thinking I came to realization that it should work more or less like this:
actual drawing of "everything" occurs after onResume()
in "free" time parts of the screen can be redrawn but only those that were invalidated (and everything underneath)
Therefore, it would seem, if I change something after onResume() (e.g. as a response to a button click, I should invalidate() the changed View).
However, from what scana in this question says, it must be more complex then that and it depends somethimes on what method one uses.
E.g. on whether one uses
lastClicked.setImageBitmap();
or
lastClicked.setImageResource();
So, when it's necessary to execute invalidate() on a View and how does it really work ?
(Do consider accepting some answers)
Generally, invalidate() means 'redraw on screen' and results to a call of the view's onDraw() method. So if something changes and it needs to be reflected on screen, you need to call invalidate(). However, for built-in widgets you rarely, if ever, need to call it yourself. When you change the state of a widget, internal code will call invalidate() as necessary and your change will be reflected on screen. For example, if you call TextView.setText(), after doing a lot of internal processing (will the text fit on screen, does it need to be ellipsised, etc.), TextView will call invalidate() before setText() returns. Similarly for other widgets.
If you implement a custom view, you will need to call invalidate() whenever the backing model changes and you need to redraw your view. It can also be used to create simple animations, where you change state, then call invalidate(), change state again, etc.
Usually, the system handles resizing, hiding, showing and a ton of other things for your widgets automatically but it sometimes has issues if the underlying buffer for drawn pixels or backing data has changed or is stale (you swap the image resource on a View or the raw dataset changes). This occurs because there is no way that the OS can know that the data changed in the specific manner that it did.
In these cases where you are dealing with drawing, you have to tell the system that its underlying data is not in a good state with Widget.invalidate() and the re-drawing gets queued on the main thread just as you mentioned. Depending on the system implementation and Android version what is tracked for changes by the system varies but what I normally do is assume that system resources (byte arrays, char arrays, resource indexes, manual drawing on the context) are not tracked and need an invalidate and everything else will be handled by the system.
Please remember that drawing on the screen is frequent process, whenever you update a view, that change should be propogated and redrawn to notify such change right. invalidate() is a trigger method,that signals force reDrawing of any view you wish to show changes for.
I had this problem when I wanted to draw a textPaint!
My code was
canvas.drawPaint(textPaintNumber)
canvas.drawText("MyText", 30F, 63F, textPaintNumber)
I cleared the first lint and the problem was solved
canvas.drawText("MyText", 30F, 63F, textPaintNumber)
I'm reading up on SurfaceView and how to use it, and I've come across some information that states that a SurfaceView has View#willNotDraw() set to false by default, and that it's up to you to call SurfaceView#onDraw(). I also read that RomainGuy said that this is done by default because it is more efficient. My question now is, when should you handle calling SurfaceView#onDraw() in a separate thread, and when should you just set View#willNotDraw() to true, and just call SurfaceView#invalidate(). Is there a difference between the two, and does one improve performance more than the other?
See:
http://developer.android.com/reference/android/view/View.html#setWillNotDraw(boolean)
I'm not sure where you got your information, but at least the javadoc says that most users will set this to false to get Android to send it onDraw events itself. As for your question about when you should do this, I would say it comes down to why you're using a SurfaceView.
If your view is displaying something dynamic (e.g. for a game or something that has a tight event loop), you'll want to be controlling exactly when updates happen, especially if you'll have the information to use one of the more detailed forms of invalidate to save redrawing the entire View. You won't want Android to call invalidate for you, and that's why the flag is there.
If, on the other hand, you are simply drawing something static, it makes sense to let Android's UI stack control the invalidations.
By the way, invalidate only posts a request to re-draw the View, so be aware of this if you intend to use the event-loop style (onDraw will be called sometime after you call it).
Edit: some clarifications.
Using SurfaceView.onDraw() and SurfaceView.invalidate() will make SurfaceView behave like a normal View and you will pay for the extra overhead associated with SurfaceView. If you want to draw from the UI thread, use a regular View instead. It's easier and cheaper.
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.
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.