Reputation: 61
I have a SwipeRefreshLayout view as a parent. Inside it i am using a webview that loads web pages. I want to reload webview by pulling down SwipeRefreshLayout. Here is my xml:
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
and java code:
mWebView = (WebView) view.findViewById(R.id.webView);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl("http://kashirskoe.vegas-city.ru/renter/");
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh_layout);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.postDelayed(new Runnable() {
@Override
public void run() {
mWebView.reload();
mSwipeRefreshLayout.setRefreshing(false);
}
}, 1000);
}
});
At the first blush it works perfect. But when the user is scrolling down for example a map control on the web page it's not scrolling. Instead of this SwipeRefreshLayout intercept scrolling and refresh the web page (the example page url is on the code).
I want to disable SwipeRefreshLayout scroll when any web control on the page is scrolling. Like it works in browser Google Chrome.
I think i need to inject and execute JavaScript in the webview in order to detect scroll events on any scroll control on the page. And pass it to android native code. I know how to pass it to native code.
But how can i detect this scroll events in javascript?
Upvotes: 6
Views: 4927
Reputation: 199
The webview's content is scrolling perfect. I want to detect scrolling of any element on the web page, but not all webview's content. For example on the page besides others lay out map element. And I can scroll it. But when SwipeRefreshLayout is added, the scrolling of the map element is intercepted.
According to this comment from you, I think I've dealt with a similar issue. And here is my solution for detecting an element scrolling in the webview.
the solution adds a onTouchStart listener into the webview after the webview finishes loading to record the valid scrollable touch on an element or its ancestor in the web page then sends the necessary information of the touch back to your native codes for further actions.
Firstly, we need to enable JavaScript of the webview and add a javascript interface for the webview to receive the touch information. It would be something like this in Kotlin:
webview.settings.javaScriptEnabled = true
webview.addJavascriptInterface(scrollableWebViewJSInterfaceObject, "scrollableWebViewJSInterface") // We name the interface as scrollableWebViewJSInterface
And in this scrollableWebViewJSInterfaceObject
, we need to implement two functions as the javascript interface callback like this:
@JavascriptInterface
@Suppress("UNUSED") // Used by javascript callback
fun touchOnScrollableWebElement(
scrollLeft: Int,
scrollTop: Int,
scrollLeftMax: Int,
scrollTopMax: Int
) {
// May check whether the element can scroll according to its scrolling direction.
// And if it can scroll, you should swallow the touch here and disallow your SwipeRefreshLayout to scroll.
// Otherwise, let SwipeRefreshLayout scroll.
}
@JavascriptInterface
@Suppress("UNUSED") // Used by javascript callback
fun touchOnNotScrollableWebElement() {
// If a touch in the web view does not on any scrollable element.
// Just let SwipeRefreshLayout do whatever it wants.
}
if a touch in the web view is on a scrollable element, touchOnScrollableWebElement()
is called for further checks. Otherwise, touchOnNotScrollableWebElement()
is called.
Secondly, we should add the touch listener in JS after the webview finishes loading the page.
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
registerWebElementsTouchCallback(view)
}
private fun registerWebElementsTouchCallback(view: WebView) {
// Add a "touchstart" listener to watch all touched elements.
// If a touch down on an element, we check whether the element is scrollable or anyone of
// its ancestors is scrollable in order to let the webview's scrollable container know whether it can scroll.
// The elements rendered in the web view are not immediate descendant views of the web view
// and cannot be accessed directly in android codes. As a result, to determine whether they
// are scrollable and can be scrolling at the time, we have to add javascript interface and
// execute javascript codes here.
val javascript = "function onTouchStart(event) { \n" +
"if (event.targetTouches.length >= 1) { \n" +
"var touch = event.targetTouches[0]; \n" +
"if (touch.target) { \n" +
"var node = touch.target; \n" +
"while(node) { \n" +
"var scrollLeftMax = node.scrollWidth - node.clientWidth; \n" +
"var scrollTopMax = node.scrollHeight - node.clientHeight; \n" +
"if (scrollLeftMax > 0 || scrollTopMax > 0) { \n" +
"window.scrollableWebViewJSInterface.touchOnScrollableWebElement(node.scrollLeft, " +
"node.scrollTop, scrollLeftMax, scrollTopMax); \n" +
"break; \n" +
"} \n" +
"node = node.parentNode; \n" +
"} \n" +
"if (!node) { \n" +
"window.scrollableWebViewJSInterface.touchOnNotScrollableWebElement(); \n" +
"} \n" +
"} \n" +
"} \n" +
"} \n" +
"window.addEventListener('touchstart', onTouchStart, true);\n"
view.evaluateJavascript(javascript, null)
}
As you can see, the JS codes check whether an element can scroll by its scrollWidth and clientWidth, and then call the corresponding JS interface functions. Further information about these variables can be found in the documentation. I think except for the string format, the codes are very straightforward.
Lastly, this solution should work for your case if it's applied correctly.
Hope this can help whoever faces the similar issues.
Upvotes: 1
Reputation: 1042
You can detect if WebView's content is scrolling by defining a custom WebView class that extends WebView and overriding the method onOverScrolled
doc here.
Start with SwipeRefreshLayout disabled and enable it when content in the WebView is not scrolling. Then, you can detect the scrolling gesture start by catching a touch DOWN event occurring on the WebView.
public class MyWebView extends WebView{
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
if( clampedX || clampedY ){
//Content is not scrolling
//Enable SwipeRefreshLayout
ViewParent parent = this.getParent();
if ( parent instanceof SwipeRefreshLayout) {
((SwipeRefreshLayout)parent).setEnabled(true);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if(event.getActionMasked()==MotionEvent.ACTION_DOWN){
//Disable SwipeRefreshLayout
ViewParent parent = this.getParent();
if ( parent instanceof SwipeRefreshLayout) {
((SwipeRefreshLayout)parent).setEnabled(false);
}
}
return true;
}
}
Upvotes: 17