Can't scroll over image in webview

前端 未结 4 614
北海茫月
北海茫月 2021-01-21 22:25

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

相关标签:
4条回答
  • 2021-01-21 22:56

    Try to add webview in this sample..

      <NestedScrollViewHome
                android:id="@+id/scroll_nested_journey"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_below="@id/toolbar">
    
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:descendantFocusability="blocksDescendants"
                    android:orientation="vertical">
    
        //  add Webview here
    
         </LinearLayout>
    
            </NestedScrollViewHome>
    

    NestedScrollViewHome.java

    public class NestedScrollViewHome  extends NestedScrollView {
        @SuppressWarnings("unused")
        private int slop;
        @SuppressWarnings("unused")
        private float mInitialMotionX;
        @SuppressWarnings("unused")
        private float mInitialMotionY;
        public NestedScrollViewHome(Context context) {
            super(context);
            init(context);
        }
    
        private boolean enableScrolling = true;
    
        public boolean isEnableScrolling() {
            return enableScrolling;
        }
    
        public void setEnableScrolling(boolean enableScrolling) {
            this.enableScrolling = enableScrolling;
        }
    
    
        private void init(Context context) {
            ViewConfiguration config = ViewConfiguration.get(context);
            slop = config.getScaledEdgeSlop();
        }
        public NestedScrollViewHome(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
        public NestedScrollViewHome(Context context, AttributeSet attrs,
                                  int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
        private float xDistance, yDistance, lastX, lastY;
        @SuppressWarnings("unused")
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
          if (isEnableScrolling())
          {
              final float x = ev.getX();
              final float y = ev.getY();
              switch (ev.getAction()) {
                  case MotionEvent.ACTION_DOWN:
                      xDistance = yDistance = 0f;
                      lastX = ev.getX();
                      lastY = ev.getY();
                      // This is very important line that fixes
                      computeScroll();
                      break;
                  case MotionEvent.ACTION_MOVE:
                      final float curX = ev.getX();
                      final float curY = ev.getY();
                      xDistance += Math.abs(curX - lastX);
                      yDistance += Math.abs(curY - lastY);
                      lastX = curX;
                      lastY = curY;
                      if (xDistance > yDistance) {
                          return false;
                      }
              }
              return super.onInterceptTouchEvent(ev);
          }else
              return false;
        }
        public interface OnScrollChangedListener {
            void onScrollChanged(NestedScrollView who, int l, int t, int oldl,
                                 int oldt);
        }
        private OnScrollChangedListener mOnScrollChangedListener;
        public void setOnScrollChangedListener(OnScrollChangedListener listener) {
            mOnScrollChangedListener = listener;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (isEnableScrolling()) {
                return super.onTouchEvent(ev);
            } else {
                return false;
            }
        }
    
        @Override
        protected void onScrollChanged(int l, int t, int oldl, int oldt) {
            super.onScrollChanged(l, t, oldl, oldt);
            if (mOnScrollChangedListener != null) {
                mOnScrollChangedListener.onScrollChanged(this, l, t, oldl, oldt);
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-21 23:00

    Have you tried setting this to your webView?

     webview.setVerticalScrollBarEnabled(true);
     webview.setHorizontalScrollBarEnabled(true);
    
    0 讨论(0)
  • 2021-01-21 23:12

    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)

    import android.view.GestureDetector;
    import android.view.MotionEvent;
    
    public class OnSwipeListener extends GestureDetector.SimpleOnGestureListener {
    
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    
        // Grab two events located on the plane at e1=(x1, y1) and e2=(x2, y2)
        // Let e1 be the initial event
        // e2 can be located at 4 different positions, consider the following diagram
        // (Assume that lines are separated by 90 degrees.)
        //
        //
        //         \ A  /
        //          \  /
        //       D   e1   B
        //          /  \
        //         / C  \
        //
        // So if (x2,y2) falls in region:
        //  A => it's an UP swipe
        //  B => it's a RIGHT swipe
        //  C => it's a DOWN swipe
        //  D => it's a LEFT swipe
        //
    
        float x1 = e1.getX();
        float y1 = e1.getY();
    
        float x2 = e2.getX();
        float y2 = e2.getY();
    
        Direction direction = getDirection(x1,y1,x2,y2);
        return onSwipe(direction, velocityX, velocityY);
    }
    
    /** Override this method. The Direction enum will tell you how the user swiped. */
    public boolean onSwipe(Direction direction, float velocityX, float velocityY){
        return false;
    }
    
    /**
     * Given two points in the plane p1=(x1, x2) and p2=(y1, y1), this method
     * returns the direction that an arrow pointing from p1 to p2 would have.
     * @param x1 the x position of the first point
     * @param y1 the y position of the first point
     * @param x2 the x position of the second point
     * @param y2 the y position of the second point
     * @return the direction
     */
    public Direction getDirection(float x1, float y1, float x2, float y2){
        double angle = getAngle(x1, y1, x2, y2);
        return Direction.fromAngle(angle);
    }
    
    /**
     *
     * Finds the angle between two points in the plane (x1,y1) and (x2, y2)
     * The angle is measured with 0/360 being the X-axis to the right, angles
     * increase counter clockwise.
     *
     * @param x1 the x position of the first point
     * @param y1 the y position of the first point
     * @param x2 the x position of the second point
     * @param y2 the y position of the second point
     * @return the angle between two points
     */
    public double getAngle(float x1, float y1, float x2, float y2) {
    
        double rad = Math.atan2(y1-y2,x2-x1) + Math.PI;
        return (rad*180/Math.PI + 180)%360;
    }
    
    
    public enum Direction{
        up,
        down,
        left,
        right;
    
        /**
         * Returns a direction given an angle.
         * Directions are defined as follows:
         *
         * Up: [45, 135]
         * Right: [0,45] and [315, 360]
         * Down: [225, 315]
         * Left: [135, 225]
         *
         * @param angle an angle from 0 to 360 - e
         * @return the direction of an angle
         */
        public static Direction fromAngle(double angle){
            if(inRange(angle, 45, 135)){
                return Direction.up;
            }
            else if(inRange(angle, 0,45) || inRange(angle, 315, 360)){
                return Direction.right;
            }
            else if(inRange(angle, 225, 315)){
                return Direction.down;
            }
            else{
                return Direction.left;
            }
    
        }
    
        /**
         * @param angle an angle
         * @param init the initial bound
         * @param end the final bound
         * @return returns true if the given angle is in the interval [init, end).
         */
        private static boolean inRange(double angle, float init, float end){
            return (angle >= init) && (angle < end);
        }
    }
    }
    

    Then use in your activity...

    public class FullscreenActivity extends AppCompatActivity implements View.OnTouchListener {
    
    private WebView blizzView;
    private Button backButton;
    private String website;
    private GestureDetector gestureDetector;
    
    /**
     * 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();
        }
    };
    
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.d("SWIPER", "onTouch: ");
        gestureDetector.onTouchEvent(event);
        return false;
    }
    public void scrollUpwards(int scroll_speed) {
        try{
            ObjectAnimator anim = ObjectAnimator.ofInt(blizzView, "scrollY", blizzView.getScrollY(), blizzView.getScrollY() - scroll_speed);
            anim.setDuration(800).start();
        }catch (Exception ex){}
    }
    public void scrollDownwards(int scroll_speed) {
        try{
           // float maxScrollY = blizzView.getContentHeight() * blizzView.getScale() - blizzView.getMeasuredHeight();
           if(blizzView.getScrollY() <= 100){
               //due to the scroll animation the web page slows down at the top
               //hence we add some extra speed when user is at the top of page to prevent from
               //experiencing delay when scrolling back down
               scroll_speed += 1300;
           }
            ObjectAnimator anim = ObjectAnimator.ofInt(blizzView, "scrollY", blizzView.getScrollY(), blizzView.getScrollY() + scroll_speed);
            anim.setDuration(800).start();
        }catch (Exception ex){}
    }
    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        gestureDetector=new GestureDetector(this,new OnSwipeListener(){
    
            @Override
            public boolean onSwipe(Direction direction, float velocityX, float velocityY) {
              //  int scroll_jump_percentage = 60 / 100; //percent of velocity: increase(60) for faster scroll jump
                int scroll_speed =  ((int) Math.abs(velocityY)) * 60 / 100; //percent of velocity: increase(60) for faster scroll jump;
                if (direction==Direction.up){
                    //do your stuff
                 //   Log.d("SWIPER", "onSwipe: up");
                    scrollDownwards(scroll_speed);
                    Toast.makeText(getApplicationContext(),"You swiped UP ("+scroll_speed+")",Toast.LENGTH_SHORT).show();
    
                    //we made it!
                    return true;
                }
                else if (direction==Direction.down){
                    //do your stuff
                  //  Log.d("SWIPER", "onSwipe: down");
                    scrollUpwards(scroll_speed);
                    Toast.makeText(getApplicationContext(),"You swiped DOWN ("+scroll_speed+")",Toast.LENGTH_SHORT).show();
    
                    //we made it!
                    return true;
                }
    
                //nothing to handle here... Mr. WebView can do the rest
                return false;
            }
        });
        mVisible = true;
        website = "https://www.blizz-z.de";
        blizzView = findViewById(R.id.blizzView);
        blizzView.setOnTouchListener(this);
        WebSettings settings = blizzView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setBuiltInZoomControls(false);
    
        // 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);"
                        + "});");
    
                //inject script (this script would remove all event listener from product image
                // that was placed by your MagicZoom Js Library
                //TEMP FIX
               /* blizzView.loadUrl(
                        "javascript:(function() { " +
                                "var slides = document.getElementsByClassName(\"MagicZoom\");\n" +
                                "for(var i = 0; i < slides.length; i++)\n" +
                                "{\n" +
                                "var old_element = slides.item(i);\n" +
                                "old_element.href = '#';\n" +
                                "                var new_element = old_element.cloneNode(true);\n" +
                                "\t\t\t\told_element.parentNode.replaceChild(new_element, old_element);\n" +
                                "       \n" +
                                "   }" +
                                "})()");*/
    
            }
    
            // 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;
            }
    
        });
        // 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);
    }
    
    }
    

    Option Two

    Add the below block into onPageFinished method of your WebviewClient

    //This will remove all javascript event listener from affected images. (offending script: MagicZoomPlus.js)
    //Setback of this is that images would no longer zoom in when clicked. 
    
                blizzView.loadUrl(
                        "javascript:(function() { " +
                                "var slides = document.getElementsByClassName(\"MagicZoom\");\n" +
                                "for(var i = 0; i < slides.length; i++)\n" +
                                "{\n" +
                                "var old_element = slides.item(i);\n" +
                                "old_element.href = '#';\n" +
                                "                var new_element = old_element.cloneNode(true);\n" +
                                "\t\t\t\told_element.parentNode.replaceChild(new_element, old_element);\n" +
                                "       \n" +
                                "   }" +
                                "})()");
    
    0 讨论(0)
  • 2021-01-21 23:15

    At first remove this entire section,

    @Override
            public void onPageFinished(WebView view, String url)
            {
                super.onPageFinished(view, url);
    
                //blizzView.Settings.setSupportZoom(true);
    
                blizzView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
    
                blizzView.getSettings().setSupportZoom(true);
                blizzView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
    
                blizzView.loadUrl(
                        "javascript:(function() {"
    
    
                                //+ "jQuery('.logo').remove();" //Einige Funktionen wie z.B. alert() sind scheinbar aus Sicherheits-/Missbrauchsgründen nicht verfügbar; jQuery kann man natürlich nur nutzen wenn es eingebunden ist. Möglicherweise kann man es auch selbst einbinden.
                        + "})()"
                );
    
                // 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);
                }
    
                blizzView.setVerticalScrollBarEnabled(true);
                blizzView.setHorizontalScrollBarEnabled(true);
    
            }
    

    because you can't rely on onPageFinished instead you should implement shouldOverrideUrlLoading. I'll post my complete code here.

    public class TempWebActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_temp_web);
    
        WebView webView = findViewById(R.id.wv_scroller);
        webView.getSettings().setJavaScriptEnabled(true);
    
        webView.loadUrl("https://www.blizz-z.de");
    
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
        }
    }
    

    Here's the activity layout

    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".TempWebActivity">
    
    <WebView 
    android:id="@+id/wv_scroller"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
    
    </ScrollView>
    

    The key to achieve this is to use a ScrollView wrapper with WebView if some Javascript can intercept some touch events. Also make sure that you keep the height of WebView as wrap_content. Happy coding bro! :)

    0 讨论(0)
提交回复
热议问题