10

我阅读了很多关于 Zooming in WebViews 主题的票,并没有为我的案例找到答案。

这是我的设置:

我正在使用通常具有以下设置的自定义网络视图:

getSettings().setBuiltInZoomControls(false);
getSettings().setSupportZoom(false);
getSettings().setUseWideViewPort(true);
getSettings().setLoadWithOverviewMode(true);

让我在这里注意一下,我依赖 OverviewMode 以及 WideViewPort 来缩放我的 WebView。

我还覆盖了我的 OnTouchEvent 并将所有合适的事件委托给手势检测器:

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (gestureDetector.onTouchEvent(event)) return true;
    return super.onTouchEvent(event);
  }

这是拦截所有 doubleTap 事件的侦听器实现:

  @Override
  public boolean onDoubleTapEvent(MotionEvent e) {
    // Do nothing! 
    return true;
  }

  @Override
  public boolean onDoubleTap(MotionEvent e) {
    // Do nothing! 
    return true;
  }

  @Override
  public boolean onSingleTapConfirmed(MotionEvent e) {
    // Do nothing! 
    return true;
  }

我还覆盖了这两个与缩放相关的 WebView 方法:

  @Override
  public boolean zoomIn() {
    return true;
  }

  @Override
  public boolean zoomOut() {
    return true;
  }

除了所有这些选项,特定的点击频率会导致我的 web 视图放大/缩小。我还没有找到禁用这种缩放的选项,此缩放的 MotionEvent 似乎不适用于 GestureDetector 并且覆盖 zoomIn() zoomOut() 方法也没有效果。

任何人都可以帮助我避免这种 WebView 的双击缩放行为吗?

4

6 回答 6

7

有两种方法可以实现您的目标:

方法一

像这样实现GestureDetector.OnDoubleTapListener

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
  return false; //Nothing
}

@Override
public boolean onDoubleTap(MotionEvent e) {
  //Indicates that this implementation has handled the double tap.
  return true;
}

@Override
public boolean onDoubleTapEvent(MotionEvent e) {
  //Indicates that this implementation has handled the double tap.
  return true;
}

并将其附加到您的GestureDetector像这样:

gestureDetector.setOnDoubleTapListener(this);

方法二

您还可以使用WebSettings.setUseWideViewPort(false);并手动计算视图的大小。

这些方法应该可以帮助您实现显示所有内容的不可缩放的 Web 视图。

public int getWindowWidth(Activity activity) {
  Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
  Point size = new Point();
  display.getSize(size);
  int width = size.x;
  return width;
}

public int getInitialScale(Activity activity, int websiteWidth) {
  return (getWindowWidth(activity) / websiteWidth) * 100;
}
于 2012-09-07T11:26:41.127 回答
2

对于您的问题,有一个基于 Javascript 的绝妙解决方案(因此,如果是这种情况,您必须能够访问远程端的 HTML/JS 代码)。

使用FastClick 库,您只需添加 .js 文件,然后调用它:

<script type="application/javascript" src="fastclick.js"></script>

<script language="javascript">

window.addEventListener('load', function() {
    FastClick.attach(document.body);
}, false);

</script>

这将摆脱双击缩放,并且(在我看来)仍然有一个巨大的好处:所有点击都快 0.3 秒,因为系统不必再等待双击!在 Android 上查看此示例以查看差异:Practical Example

好吧,我不知道这个解决方案是否适合你的情况,但它是我的 Webview 项目的完美解决方案。希望能帮助到你!

ps1:你必须在所有页面和框架中添加上面的代码

ps2:捏缩放会正常工作

于 2014-02-28T05:22:38.393 回答
1

您需要通过以下方式覆盖 WebView 上的 OnTouchListener

    wv.setOnTouchListener(this);

并且内部方法 onTouch 只是检查它是否检测到双标签然后通过强制忽略 webview 中的缩放以返回 true

@Override
public boolean onTouch(View v, MotionEvent event) {
    // TODO Auto-generated method stub
    onTouchEvent(event);

    if (doubletab)
        return true;

    return false;
}

你可以看到这样的完整代码:MainActivity.java

package com.example.testwebview;

import android.os.Bundle;
import android.app.Activity;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.View.OnTouchListener;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.TextView;

public class MainActivity extends Activity implements OnGestureListener, OnTouchListener, GestureDetector.OnDoubleTapListener
{

    TextView tv;
    WebView wv;
    GestureDetector gd;

    boolean doubletab = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        gd = new GestureDetector(this);

        setContentView(R.layout.activity_main);

        tv = (TextView)findViewById(R.id.textView1);
        wv = (WebView)findViewById(R.id.webView1);

        WebSettings setting = wv.getSettings();
        setting.setBuiltInZoomControls(false);
        setting.setSupportZoom(false);
        setting.setUseWideViewPort(true);
        setting.setLoadWithOverviewMode(true);

        wv.setOnTouchListener(this);
        wv.loadUrl("http://www.sanook.com");
    }
    @Override
    public boolean onDoubleTap(MotionEvent arg0) {
        tv.setText("double tap");
        doubletab = true;
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent arg0) {
        tv.setText("double tap event");
        doubletab = true;
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent arg0) {
        tv.setText("single tap confirm");
        doubletab = false;
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent me)
    {
        return gd.onTouchEvent(me);
    }

    @Override
    public boolean onDown(MotionEvent arg0) {
        tv.setText("down");
        doubletab = false;
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
            float arg3) {
        tv.setText("fling");
        doubletab = false;
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void onLongPress(MotionEvent arg0) {
        tv.setText("long press");
        doubletab = false;
        // TODO Auto-generated method stub

    }

    @Override
    public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
            float arg3) {
        tv.setText("scroll");
        doubletab = false;
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void onShowPress(MotionEvent arg0) {
        // TODO Auto-generated method stub
        tv.setText("show press");
        doubletab = false;

    }


    @Override
    public boolean onSingleTapUp(MotionEvent arg0) {
        // TODO Auto-generated method stub
        tv.setText("single tab up");
        doubletab = false;
        return false;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // TODO Auto-generated method stub
        onTouchEvent(event);

        if (doubletab)
            return true;

        return false;
    }
}

和activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="TextView" />

    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/textView1" />

</RelativeLayout>

不要忘记在 manifest.xml 中添加权限

<uses-permission android:name="android.permission.INTERNET" />
于 2012-09-11T04:50:05.607 回答
0

另一种方法(唯一对我有用的方法)是模拟双击之间的远程中间点击,以便 WebView 不会将它们重新识别为结果。负坐标可用于此目的,尽管小于 -1 的任何值都会减慢甚至破坏该过程。因此,我们至少需要两个点,例如 (0, -1) 和 (2*d+1, -1),其中d是轻敲之间的最大距离,以便将它们视为双击。

webView.setOnTouchListener(new View.OnTouchListener() {
        private long lastUp = -1000;
        private float lastDownX = -1000;
        private float lastDownY = -1000;
        private int dtDistance = 0;         
        private int dtDistanceSquared = 0;
        private long dtTime = 0;

        private void performTap(View v, int x, int y, long t) {             
            MotionEvent e_down = MotionEvent.obtain(t, t, MotionEvent.ACTION_DOWN, x, y, 0);
            v.dispatchTouchEvent(e_down);
            e_down.recycle();

            MotionEvent e_up = MotionEvent.obtain(t, t, MotionEvent.ACTION_UP, x, y, 0);
            v.dispatchTouchEvent(e_up);
            e_up.recycle();
        }

        private int getRemoteX(float x) {
            return Math.round(x > dtDistance + 0.5 ? 0 : 2 * dtDistance + 1);
        }

        private boolean inRadius(int x0, int y0, float x1, float y1) {
            boolean result = (x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1) <= dtDistanceSquared;
            return result;
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) { 
            if (event.getY() >= 0)  // Otherwise it's a fake tap we simulated
            {               
                if (dtTime == 0)
                {
                    dtDistance = ViewConfiguration.get(v.getContext()).getScaledDoubleTapSlop();    // Maximum distance between taps for them to be considered double tap
                    dtDistanceSquared = dtDistance * dtDistance;
                    dtTime = ViewConfiguration.getDoubleTapTimeout();   // Maximum time elapsed between taps for them to be considered double tap
                }

                switch (event.getAction())
                {
                    case MotionEvent.ACTION_UP:
                        lastUp = event.getEventTime();
                        break;
                    case MotionEvent.ACTION_DOWN:
                        long t = event.getEventTime(); 
                        if (t - lastUp < dtTime * 4/3)  // Very rarely just (t - lastUp <= dtTime) doesn't work
                        {
                            int x = getRemoteX(event.getX());
                            if (inRadius(x, -1, lastDownX, lastDownY))
                                performTap(v, getRemoteX(lastDownX), -1, t);    // Otherwise our fake tap would constitute a double tap with the previous real tap
                            performTap(v, x, -1, t);                                
                        }
                        lastDownX = event.getX();
                        lastDownY = event.getY();
                        break;                      
                }
            }

            return false;                                   
        }
    });
于 2014-01-03T12:03:03.203 回答
0

如果可能,请替换 html 中的静态元标记:

<script>
 var meta = document.createElement("meta");
 meta.setAttribute('name','viewport');
 meta.setAttribute('content','width=device-width, user-scalable=no');
 document.getElementsByTagName('head')[0].appendChild(meta); 
</script>

此外,您可以使用一个不错的脚本:FastClick
它不会等待点击事件,因此它更快。

<script type="application/javascript" src="fastclick.js"></script>

    <script language="javascript">

        window.addEventListener('load', function() {
            FastClick.attach(document.body);
        }, false);

</script>

然后为您的手势检测器设置一个双击监听器。(在自定义 WebView 中)

gestureDetector.setOnDoubleTapListener(new OnDoubleTapListener() {

    @Override
    public boolean onSingleTapConfirmed( MotionEvent e ) {
        return false;
    }

    @Override
    public boolean onDoubleTapEvent( MotionEvent e ) {

        return true;
    }

    @Override
    public boolean onDoubleTap( MotionEvent e ) {

        return true;
    }
});

覆盖 onTouchEvent 方法(在自定义 WebView 中):

@Override
public boolean onTouchEvent( MotionEvent event ) {

    boolean gestureHandled = gestureDetector.onTouchEvent(event);
    int actionMask = event.getActionMasked() & MotionEvent.ACTION_MASK;
    int index = ( event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    int pointId = event.getPointerId(index);

    // ignore move events
    if (actionMask == MotionEvent.ACTION_MOVE) {

        return super.onTouchEvent(event);
    }

    // cancel detected double taps
    if (gestureDetector.onTouchEvent(event)) {
        event.setAction(MotionEvent.ACTION_CANCEL);
        super.onTouchEvent(event);
        return true;
    }

    // if you want to ignore multi touch events/taps
    if (pointId != 0) {

        System.out.println("KEY multiple points detected");
        return true;
    }

    // use single taps
    if (event.getAction() == MotionEvent.ACTION_UP) {

        event.setAction(MotionEvent.ACTION_CANCEL);
        super.onTouchEvent(event);
        event.setAction(MotionEvent.ACTION_DOWN);
        super.onTouchEvent(event);
        event.setAction(MotionEvent.ACTION_UP);
        return true;
    }

return super.onTouchEvent(event);
}
于 2015-02-11T12:34:29.133 回答
-3

尝试将 onDoubleTapEvent onDoubleTap 的返回值设为 false....

@Override
  public boolean onDoubleTapEvent(MotionEvent e) {
    // Do nothing! 
    return false;
  }

  @Override
  public boolean onDoubleTap(MotionEvent e) {
    // Do nothing! 
    return false;
  }
于 2012-09-06T14:03:34.887 回答