WebView摆脱双击缩放。

时间:2012-09-06 13:58:39

标签: android webview zoom ontouchevent motionevent

我读了许多关于在WebViews中缩放主题的门票,并没有找到我的案例的答案。

这是我的设置:

我使用的是自定义网页视图,通常包含以下设置:

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

让我在这里注意,我依赖于OverviewMode以及WideViewPort来扩展我的WebView。

我也覆盖了我的OnTouchEvent,并将所有合适的事件委托给Gesture探测器:

  @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;
  }

除了所有这些选项之外,某个抽头频率将导致我的webview放大/缩小。 我没有找到一个禁用这种缩放的选项,此Zoom的MotionEvent似乎不适用于GestureDetector,并且覆盖zoomIn()zoomOut()方法也没有效果。

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

6 个答案:

答案 0 :(得分:5)

实现目标有两种方法:

方法1

像这样实施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);

方法2

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

这些方法可以帮助您实现显示所有内容的非可缩放网页视图。

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;
}

答案 1 :(得分:2)

基于Javascript,您的问题有一个很棒的解决方案(因此,如果是这样的话,您必须能够访问远程端的HTML / JS代码)。

使用FastClick library,你需要做的就是添加.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:捏拉变焦将继续正常工作

答案 2 :(得分: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" />

答案 3 :(得分:0)

另一种方法(唯一适用于我的方法)是模拟双击之间的远程中间点击,以便WebView不会将它们作为结果进行识别。负坐标可用于此目的,尽管小于-1的任何东西都会减慢甚至打破过程。所以我们需要至少两个点,比如说(0,-1)和(2 * d + 1,-1),其中 d 是它们被认为是双击的点击之间的最大距离。 / p>

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;                                   
        }
    });

答案 4 :(得分: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);
}

答案 5 :(得分:-3)

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

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

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