webview loaddata scroll setting not updating till clicked - android

I update the html in a webView using loadData() followed by a reload(). I get the new data to display but the scroll of the page has not updated. I want to keep the view of the page at the bottom. I can wait a long time and it still doesn't update. Once I click the webView the scroll bars adjust and I can drag to the bottom of the page and see the new text.
Things I have tried:
- multiple settings and calls in onPageFinished(). None worked because the getContentHeight() hasn't changed yet. I have also tried to scrollTo(100 + getContentHeight) and that doesn't work.
- pageDown(true)
calling requestLayout() after reload()
browser.requestFocusFromTouch() - still no good, thinking i could simulate my click.
adding a few br tags. This worked a bit, but the number of br's has to equal the number of lines of new text. Since the number of new lines is not something that can be exactly determined based on orientation and screen variables it's not a good fix.
Any ideas? TIA

If you want the webview to be scrolled to the bottom after it's finished loading, I found I had to use pageDown(true) in onNewPicture. (I believe that's the event fired when a view is finished drawing.)
So the code looks something like
webView.setPictureListener( new WebView.PictureListener() {
public void onNewPicture(WebView view, Picture picture) {
webView.pageDown(true);
}
}
Using pageDown has the unfortunate problem of visibly scrolling the page; I'm having problems finding any other way to jump to the bottom of the webView, found this question while looking for a solution! (I haven't tried reaching inside the WebView and using javascript yet, though.)

Related

Webview inside RecyclerView is showing blank screen sometimes on Nougat devices only

In my Nougat device, webview inside RecyclerView is blank sometimes. When I scroll slowly and then go back to webview item content disappear. There is no issue on devices below Android N. Android N uses Chrome as the default browser for apps. So I thought there might be a bug in Chrome so I raise a bug in chrome portal as well. There are a couple of related question in SO but that didn't solve my problem. So is there a way in Android webview setting which can solve this problem? I have written detail description in the bug link.
Bug link: click here
My onBindViewHolder method code for WebView is
final VHItem vhItem = (VHItem) holder;
vhItem.webViewChild.getSettings().setUseWideViewPort(false);
vhItem.webViewChild.getSettings().setJavaScriptEnabled(true);
vhItem.webViewChild.loadData("<body>" + html + "</body>", "text/html;charset=utf-8", "utf-8");
where
html is the html string
Update
They have fixed the issue. If you are still having the same problem try updating your Android chrome version to 61 or above.
For a start webview consumes memory because it load has to load and render html data. Rather than using a webview in a recycler view, I think it would be better if you implemented it either of these two ways:
You handle the list of data in html and send it into the webview and remove the recycler view completely
You draw the layout of the expected contents in xml and inflate it directly into the recyclerview and remove webview completed. (Note: you can inflate different views into a recycler depending on the adapter position and data at that position).
Using a webview might seem the easy way to implement whatever you are trying but trust me, the drawbacks outweight the benefit. So its just best to avoid it.
This problem can arrive for many possible reasons
When you scroll very fast
Recyclerview is purely based on Inflating the view minimal times and reusing the existing views. This means that while you are scrolling when a view(An item) exits your screen the same view is bought below just by changing its contents.When you load from internet it's always better to first download all the data and then display it. Webview's consume a lot of data and its totally Against the design principle to have them in a Recyclerview.
To repair this you could possibly add some button to reload data or refresh each time you display the view.
Nougat removed some functions from http urlconnection class
I am not sure about this one. But in one of google developer video ,T had seen something about depreciation of some functions and methods
hope You find this Helpful.
I solved this problem by floating a WebView in a up layer of RecyclerView, meanwhile placing a HOLDER view in RecyclerView.
And then I register an listener to the scroll event of RecyclerView. I Control the position and visibility of the floating WebView
You can try this code in onCreateViewHolder
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
WebView web = new WebView(parent.getContext());
web.setLayoutParams(lp);
String url="...";
WebSettings settings = web.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
web.loadUrl(url);
holder = new ViewHolder(web);

Add View to ScrollView and then scroll to bottom: Which callback is needed?

In a Dialog I have a ScrollView which contains a (vertical) LinearLayout; the bottommost View in the LinearLayout is a TextView that starts out with android:visibility="gone".
At some point during the program I want to setVisiblity(View.VISIBLE) on the TextView and display an error message. And because the ScrollView could be in any position at this point, I want to scroll to the bottom, when the error message appears.
I tried all of:
scrollView.fullScroll(View.FOCUS_DOWN);
scrollView.scrollTo(0, tvLoginError.getTop());
scrollView.scrollTo(0, tvLoginError.getBottom());
and each of those in each of these:
scrollView.post(...)
tvLoginError.post(...)
scrollView.getViewTreeObserver.addOnGlobalLayoutListener(...)
tvLoginError.getViewTreeObserver.addOnGlobalLayoutListener(...)
scrollView.getViewTreeObserver.addOnPreDrawListener(...)
tvLoginError.getViewTreeObserver.addOnPreDrawListener(...)
None of these works properly. The scolling always stops when the newly visible TextView is just outside the ScrollView or when it is halfway visible - but never truly at the bottom. (The ones with ViewTreeObserver would work in principle but only if I don't remove the callback after the first update. But if I keep it around longer, I have no way of knowing when to remove it instead (...?)
Please tell me what callback I need to use to scroll to the bottom after the TextView is completely visible.
Since I found this question when looking for the answer, I figure I'll save the next person some time.
The underlying problem is that you are calling your scroll command too early. If you add something to the view, the layout changes don't really happen until the view is redrawn. If you write content_view.getHeight to the log where you are doing the scroll call, you will find that the value is unchanged by adding to the content.
The solution is to do something like the following:
scroll_view.post(new Runnable() {
#Override
public void run() {
// Get content_view
scroll_view.smoothScrollTo(content_view.getWidth(), content_view.getHeight());
}
});
This resolves the problem because you are posting to the main event loop and it will run after the events already queued up -- which includes rendering the layout.
NOTE: I just found that in at least one case this didn't work as expected because it still ran the post runnable too early. That is quickly solved by using a short delayed post, but I'd love to understand why the above solution doesn't always work. Here is an example (Java 8 lambda version) using postDelayed:
view_port.postDelayed(()->{
LinearLayout content = scroll_view.findViewById(R.id.content);
scroll_view.smoothScrollTo(0, content.getHeight());
}, 200);
Maybe you can try this way
use "postDelayed" method
delay few millisecond then do what you want to do

How to make list view scrollable until the last element is on top of the screen

I am developing an application that needs to show calendar agenda, much like the agenda in the native calendar. I have a list view showing different events (Note: in the context of the question events is entity of my system). I want to provide a 'Today' button on this screen. When the user clicks on this button the events are supposed to be scrolled until the first event of the current's day schedule is on top of the screen. The problem occurs when I have only a few events scheduled from today on - so few that they do not fill a whole screen. Then the list view just scrolls until the last event in the calendar is on the bottom. This usually means that the desired effect of having the first today's event on top is not achieved.
Any suggestions how this can be done? I have thought of adding some blank elements at the end, but this seems ugly workaround, and furthermore it will require special device-specific calculations that will tell me how many elements to insert.
Edit:
Adding some code as requested in comment
Actually I am not sure this code will surprise anyone, but:
public void onTodayClicked(View target) {
// calculate the indexOf. It works and is not related to the question
if (indexOf >= 0) {
ListView list = (ListView) findViewById(R.id.events_list_view);
list.setSelection(indexOf);
}
}
I am not sure the layout definition is important to aid the answering of the question, but if you think so I can add it too.
You can achieve this by two ways:
call smoothScrollToPositionFromTop method
call setSelectionFromTop method
Using the smoothScroll method is better, because it actually does the transition smoothly - that means it really scrolls to it.
The only downside is that it's only available after API level 11.
The setSelectionFromTop method works since API level 1, but instead of smoothly scrolling, it jumps to the row.
If you don't need to position to the top of the screen, only to view the row, you can also use smoothScrollToPosition, which is an API level 8 call.
If you give these methods the position, which is the FIRST in the list, they will work well. (From your description I think you probably calculate the last position, but I can't be sure).

how to get the end of html page in android webview

i'm displaying a web page in android webview. and i want to trigger a message when the user reaches end of page while scrolling. how can i do that? TIA
One way is we can write custom MyWebView class that extends WebView and then we can use the function called computeHorizontalScrollRange() orcomputeVirticalScrollRange() to get the scroll range of webview.
AFAIK, I don't think there is any event that triggers this. But here's one way - you will have to try and see if it works though. Have a ListView with two rows - one having the WebViewClient and another just an ordinary TextView - You could use something like SackOfViewsAdapter for this.
In your adapter's getView(), when the function is called to display the bottom TextView, that's your cue on the user having scrolled down.

Temporary bypassing ListView scrolling

I've got a ListActivity that displays WebViews.
There is a menu option to copy text that will call this on visible WebViews.
Everything works except for one thing. I can't perform text selection in vertical direction as ListView consumes corresponding MotionEvents for it's own scrolling.
I've tried this:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (webViewsInTextSelectionMode) {
bypassEventToWebViews(ev);
return true;
} else {
return super.dispatchTouchEvent(ev);
}
}
Which will block scrolling but will also pass unadjusted MotionEvent coordinates to a WebView.
Is there a way to correctly prevent ListView from scrolling in this case?
I've got a ListActivity that displays WebViews.
That is not a good idea. WebView and ListView will compete for motion events. Putting scrolling things in scrolling things is rarely advisable in Android, particularly when they can scroll in the same direction (in this case, up and down).
There is a menu option to copy text that will call this on visible WebViews.
If you mean the accepted answer on that question, that is also not a good idea. I am dubious about the other answer, but at least it's not a bad idea prima facia.
Everything works except for one thing. I can't perform text selection in vertical direction as ListView consumes corresponding MotionEvents for it's own scrolling.
My point exactly.
Is there a way to correctly prevent ListView from scrolling in this case?
I would focus instead on getting rid of the WebViews, replacing them with TextView and Html.fromHtml(). Or, get rid of the ListView, using a single WebView to render your consolidated content.
This is a complete shot in the dark and most likely won't work but probably worth a try. You can try calling ViewParent#requestDisallowInterceptTouchEvent on the parent ListView inside of the ViewGroup containing your WebView. You'll have to call that every time there is a down motion event for the duration of the text selection. This is because the flag is reset on a up/cancel motion event.
If that doesn't work you'll probably have to create your own copy of ListView and modify it appropriately. This isn't too daunting, you'll have to copy all classes in the class hierarchy back to AdapterView and fix the copied AdapterView class (mainly use of private/package level member variables that have protected/public accessors functions).

Categories

Resources