Can't scroll over image in webview

前端 未结 4 624
北海茫月
北海茫月 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 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() {
    
                @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" +
                                "   }" +
                                "})()");
    

提交回复
热议问题