I developed a webview app for android and iOS. I noticed that I can't scroll over a specific html element in the android app, while it works on iOS.
This is the website.
The problem is on the product image but only on the products detail page where you can buy the product and put it into your cart...
HTML
<div class="magic-slide mt-active" data-magic-slide="zoom">
<a id="MagicZoomPlusImage286" class="MagicZoom" href="https://www.blizz-z.de/media/catalog/product/cache/2/image/0dc2d03fe217f8c83829496872af24a0/f/l/fliesenkleber-proflex-fix-1303_1.jpg" data-options="selectorTrigger:hover;textHoverZoomHint:Hovern zum Zoomen;textClickZoomHint:Berühren zum Zoomen;textExpandHint:Vergrößern;"
data-mobile-options="textHoverZoomHint:Berühren zum Zoomen;textClickZoomHint:Doppeltippe zum Zoomen;textExpandHint:Vergrößern;">
<figure class="mz-figure mz-hover-zoom mz-ready"><img itemprop="image" src="https://www.blizz-z.de/media/catalog/product/cache/2/image/450x450/0dc2d03fe217f8c83829496872af24a0/f/l/fliesenkleber-proflex-fix-1303_1.jpg" alt="Fliesenkleber proflex fix Schnell-Fliesenkleber - nach 3 Stunden begehbar"
style="max-width: 450px; max-height: 450px;">
<div class="mz-lens" style="top: 0px; transform: translate(-10000px, -10000px); width: 122px; height: 122px;"><img src="https://www.blizz-z.de/media/catalog/product/cache/2/image/450x450/0dc2d03fe217f8c83829496872af24a0/f/l/fliesenkleber-proflex-fix-1303_1.jpg" style="position: absolute; top: 0px; left: 0px; width: 349px; height: 349px; transform: translate(-1px, -132px);"></div>
<div
class="mz-loading"></div>
<div class="mz-hint mz-hint-hidden"><span class="mz-hint-message">Vergrößern</span></div>
</figure>
</a>
</div>
If I visit the website from the smartphones chrome browser then it works, so it must be a bug in webview?
The image is a slider, I can slide left and right but I can't scroll down the page if I scroll at the image.
FullscreenActivity.java:
package de.blizz_z.onlineshop;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.webkit.URLUtil;
import android.net.Uri;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Build;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.WebChromeClient;
import android.widget.Button;
import android.util.Log;
import android.webkit.HttpAuthHandler;
import android.webkit.ValueCallback;
/**
* An example full-screen activity that shows and hides the system UI (i.e.
* status bar and navigation/system bar) with user interaction.
*/
public class FullscreenActivity extends AppCompatActivity
{
private WebView blizzView;
private Button backButton;
private String website;
/**
* Whether or not the system UI should be auto-hidden after
* {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
*/
private static final boolean AUTO_HIDE = true;
/**
* If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after
* user interaction before hiding the system UI.
*/
private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
/**
* Some older devices needs a small delay between UI widget updates
* and a change of the status and navigation bar.
*/
private static final int UI_ANIMATION_DELAY = 300;
private final Handler mHideHandler = new Handler();
private final Runnable mHidePart2Runnable = new Runnable()
{
@SuppressLint("InlinedApi")
@Override
public void run() {
}
};
private final Runnable mShowPart2Runnable = new Runnable()
{
@Override
public void run() {
// Delayed display of UI elements
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.show();
}
}
};
private boolean mVisible;
private final Runnable mHideRunnable = new Runnable() {
@Override
public void run() {
hide();
}
};
/**
* Touch listener to use for in-layout UI controls to delay hiding the
* system UI. This is to prevent the jarring behavior of controls going away
* while interacting with activity UI.
*/
private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener()
{
@Override
public boolean onTouch(View view, MotionEvent event)
{
Log.i("debug_log", "touch");
if (AUTO_HIDE) {
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
int x = (int) event.getX();
int y = (int) event.getY();
Log.i("debug_log", "moving: (" + x + ", " + y + ")");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("debug_log", "touched down");
break;
case MotionEvent.ACTION_MOVE:
Log.i("debug_log", "moving: (" + x + ", " + y + ")");
break;
case MotionEvent.ACTION_UP:
Log.i("debug_log", "touched up");
break;
}
return true;
}
};
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen);
mVisible = true;
website = "https://www.blizz-z.de";
blizzView = findViewById(R.id.blizzView);
WebSettings settings = blizzView.getSettings();
settings.setJavaScriptEnabled(true);
// https://developer.android.com/reference/android/webkit/WebViewClient
blizzView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
Log.i("debug_log", errorResponse.getReasonPhrase());
}
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
{
super.onReceivedHttpAuthRequest(view, handler, host, realm);
view.setHttpAuthUsernamePassword(host, realm, "macs", "20macs14");
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
{
// check here the url
if (url.endsWith(".pdf")) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(browserIntent);
} else {
super.onPageStarted(view, url, favicon);
}
}
@Override
// Notify the host application that a page has finished loading.
public void onPageFinished(WebView view, String url)
{
super.onPageFinished(view, url);
// Hide/Show back button
backButton = findViewById(R.id.backButton);
backButton.setEnabled(blizzView.canGoBack());
if (blizzView.canGoBack()) {
backButton.setVisibility(View.VISIBLE);
} else {
backButton.setVisibility(View.INVISIBLE);
}
js(blizzView, "jQuery(document).ready(function() {"
+ "setInterval(function() {"
+ "jQuery('#myInput').css('background', '#'+(Math.random()*0xFFFFFF<<0).toString(16));"
+ "jQuery('a').each(function() {"
+ "jQuery(this).removeAttr('download');"
+ "});"
+ "}, 1000);"
+ "});");
}
// Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
Log.i("debug_log", "shouldOverrideUrlLoading");
//view.loadDataWithBaseURL("https://www.blizz-z.de", );
// Allow download of .pdf files
if (url.endsWith(".pdf")) {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
// if want to download pdf manually create AsyncTask here
// and download file
return true;
}
// Also allow urls not starting with http or https (e.g. tel, mailto, ...)
if( URLUtil.isNetworkUrl(url) ) {
return false;
} else {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
}
return true;
}
});
blizzView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
//WebView.HitTestResult hr = blizzView.getHitTestResult();
//Log.i("debug_log", "getExtra = "+ hr.getExtra() + " Type= " + hr.getType());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("debug_log", "touched down");
break;
case MotionEvent.ACTION_MOVE:
Log.i("debug_log", "moving: (" + x + ", " + y + ")");
break;
case MotionEvent.ACTION_UP:
Log.i("debug_log", "touched up");
break;
}
return false;
}
});
// URL laden:
blizzView.loadUrl(website);
}
@Override
protected void onPostCreate(Bundle savedInstanceState)
{
Log.i("debug_log", "onPostCreate");
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
delayedHide(100);
}
private void toggle()
{
if (mVisible) {
hide();
} else {
show();
}
}
private void hide()
{
// Hide UI first
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
mVisible = false;
// Schedule a runnable to remove the status and navigation bar after a delay
mHideHandler.removeCallbacks(mShowPart2Runnable);
mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
}
@SuppressLint("InlinedApi")
private void show()
{
// Show the system bar
mVisible = true;
// Schedule a runnable to display UI elements after a delay
mHideHandler.removeCallbacks(mHidePart2Runnable);
mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
}
/**
* Schedules a call to hide() in delay milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide(int delayMillis)
{
mHideHandler.removeCallbacks(mHideRunnable);
mHideHandler.postDelayed(mHideRunnable, delayMillis);
}
public void js(WebView view, String code)
{
String javascriptCode = "javascript:" + code;
if (Build.VERSION.SDK_INT >= 19) {
view.evaluateJavascript(javascriptCode, new ValueCallback<String>() {
@Override
public void onReceiveValue(String response) {
Log.i("debug_log", response);
}
});
} else {
view.loadUrl(javascriptCode);
}
}
// Event Listener ------------------------------------------------------------------------------
public void goBack(android.view.View view)
{
blizzView.goBack();
}
// If back button of smartphone is pressed, then go back in browser history
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
Log.i("debug_log", "KeyDown");
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (blizzView.canGoBack())
blizzView.goBack();
else
finish();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
}
activity_fullscreen.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
tools:context="de.blizz_z.onlineshop.FullscreenActivity">
<WebView
android:id="@+id/blizzView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</WebView>
<Button
android:id="@+id/backButton"
android:layout_width="90dp"
android:layout_height="39dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginBottom="16dp"
android:background="#F5EA01"
android:enabled="false"
android:onClick="goBack"
android:text="Zurück"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/blizzView"
app:layout_constraintStart_toStartOf="@+id/blizzView" />
</android.support.constraint.ConstraintLayout>
Try to add webview in this sample..
NestedScrollViewHome.java
You have two options
Option One
By creating a custom listener for Touch Events on your Webview. I have modified the snippet from @fernandohur's answer on this post to match your scenario.
Create a new class (OnSwipeListener.java)
Then use in your activity...
Option Two
Add the below block into onPageFinished method of your WebviewClient
At first remove this entire section,
because you can't rely on
onPageFinished
instead you should implementshouldOverrideUrlLoading
. I'll post my complete code here.Here's the activity layout
The key to achieve this is to use a
ScrollView
wrapper withWebView
if someJavascript
can intercept some touch events. Also make sure that you keep the height ofWebView
aswrap_content
. Happy coding bro! :)Have you tried setting this to your webView?