WebView избавляется от двойного увеличения.

Я прочитал много заявок на тему масштабирования в 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;
  }

Также я переопределил эти 2 метода WebView, связанных с масштабированием:

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

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

Тем не менее, ни одна из этих опций не позволяет увеличивать / уменьшать мой веб-просмотр. Я не нашел опцию, которая отключает этот вид масштабирования, MotionEvent для этого Zoom, кажется, не применим для GestureDetector, и переопределенные методы zoomIn() zoomOut() также не имеют никакого эффекта.

Может ли кто-нибудь помочь мне избежать поведения двойного касания в WebView?

6 ответов

Решение

Есть два метода для достижения вашей цели:

Способ 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;
}

Существует фантастическое решение для вашей проблемы, основанное на 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, чтобы увидеть разницу: Практический пример

Ну, я не знаю, подойдет ли это решение в вашем случае, но это было идеальное решение для моих проектов Webview. Надеюсь, поможет!

PS1: вы должны добавить код выше во всех страницах и фреймах

PS2: масштабирование будет работать нормально

Вам нужно переопределить OnTouchListener в вашем WebView с помощью

    wv.setOnTouchListener(this);

и внутри метода onTouch просто проверьте, что если он обнаруживает двойную вкладку, то игнорирует увеличение в веб-просмотре, принудительно возвращая 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" />

Другой подход (единственный, который работал для меня) состоял бы в том, чтобы моделировать отдаленные промежуточные касания между двойными касаниями, чтобы 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;                                   
        }
    });

Если возможно, замените статический метатег в своем 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);
}

Попробуйте сделать возвращаемые значения onDoubleTapEvent onDoubleTap как ложные....

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

  @Override
  public boolean onDoubleTap(MotionEvent e) {
    // Do nothing! 
    return false;
  }
Другие вопросы по тегам