Spedire l'evento di touch da una vista al suo fratello WebView

Ripristino del problema: spedire l'evento di contatto su un layout al suo fratello WebView che raggiunge lo stesso rotolo rispetto al WebView di default (con il fling)

Ho un frameLayout su un WebView dopo questo xml:

  • Come fare la scivolata come picasso?
  • onUpdate () variabile intilizzata sono null in onReceive della class widget
  • Come registrare il video con GPUImage?
  • come installare ndk (linux)?
  • Come aggiungere android.support.v7.widget.GridLayout in intelliJidea?
  • Android: Come posso passare i parametri su AsProExecute () di AsyncTask?
  • <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.app.ObservableWebView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/observableWebView"> </com.app.ObservableWebView> <FrameLayout android:layout_width="match_parent" android:layout_height="200dp" android:id="@+id/frameLayout"> </FrameLayout> </RelativeLayout> 

    Durante l'inizio dell'app, un segnaposto vuoto di html viene collocato da qualche parte nel WebView Dom e la sua posizione è fornita da una JavascriptInterface. Questa posizione viene convertita con il rapporto pixel e la FrameLayout è posta sopra il frameLayout. Quando il contenuto di WebView è stato spostato, il segnaposto ha spostato uno con il FrameLayout (evento inviato dal WebView osservabile). Finora, tutto funziona come dovrebbe.

    schema di layout

    Nel frameLayout devo ascoltare un clic su di esso, quindi ho installato un TouchListener seguendo questo passaggio:

     private boolean mIsNativeClick = false; private float mStartNativeX; private float mStartNativeY; private final float SCROLL_THRESHOLD = 10; private void init(){ mRootFrameLayout.setOnTouchListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mStartNativeX = event.getX(); mStartNativeY = event.getY(); mIsNativeClick = true; return true; case MotionEvent.ACTION_MOVE: if (mIsNativeClick && (Math.abs(mStartNativeX - event.getX()) > SCROLL_THRESHOLD || Math.abs(mStartNativeY - event.getY()) > SCROLL_THRESHOLD)) { mIsNativeClick = false; } break; case MotionEvent.ACTION_UP: if (mIsNativeClick) { // my action on touch up goes here return true; } } return false; } 

    L'evento a contatto arriva correttamente all'ascoltatore e tutto sta funzionando bene. Ad exception del fatto che il WebView non scorra quando scorri la FrameLayout perché è un fratello della frameLayout: non è un genitore / figlio.

    La soluzione evidente sarà quella di impostare true / false di correclty return for View.onTouchEvent o nell'elenco in modo che Android invia l'evento alla visualizzazione successiva in tre alberi di Latout. Ma perché ho bisogno di gestire l'evento down / up in FrameLayout, comincio a restituire true per MotionEvent.ACTION_DOWN smettila di submit l'evento successivo.

    Cercando su StackOverFlow durante l'ultima settimana, ho ottenuto una soluzione che funziona al 50%. Descriviamoci:


    Fase 1: fare scorrere il WebView sull'evento di spostamento FrameLayout

    La soluzione consiste nell'evitare l'intercettazione di un evento sulla FrameLayout e impostando la posizione di scorrimento del WebView in base al movimento / evento di scorrimento. Il problema con questa soluzione è che l' evento di scorrimento del rotolo non è gestito (quando: l'utente scoraggia e toglie il dito dalla schermata ha prodotto un effetto di inerzia del rotolo che continua a muoversi per qualche ms). L'aggiunta del fling è spiegato nel passaggio 2.

    Questo è lo snippet di codice: (parte per la visualizzazione personalizzata FrameLayout)

     private int movY; private float mStartNativeX; private float mStartNativeY; @Override public boolean onInterceptTouchEvent ( MotionEvent event ) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mStartNativeX = event.getX(); mStartNativeY = event.getY(); break; case MotionEvent.ACTION_SCROLL: Log.d(LOG_TAG, " scroll event"); break; } return super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(LOG_TAG, "down event : " + Float.toString(mStartNativeY)); break; case MotionEvent.ACTION_MOVE: movY = (int) ((int) mStartNativeY - event.getY()); mWebView.scrollBy(0, movY); Log.d(LOG_TAG, "move event : " + Integer.toString(movY)); return true; case MotionEvent.ACTION_UP: Log.d(LOG_TAG, "up event : "); break; default: break; } return true; } 

    Fase 2: la fuga

    Aggiungendo il fling al manul scrollBy non è così facile. L'elemento fondamentale è quello di aggiungere un GestureDetector e uno Scroller. Quindi ho una class che implementaing Runnable con uno Scroller che gestisce l'effetto fling. Questa class viene chiamata l'onFling del GestoreDetector Listener.

     private float mStartNativeY; private GestureDetector mGestureDetector; int movY; @Override public boolean onInterceptTouchEvent ( MotionEvent event ) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mStartNativeY = event.getY(); break; case MotionEvent.ACTION_SCROLL: Log.d(LOG_TAG, " scroll event"); break; } return super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { mGestureDetector.onTouchEvent(event); // 1.) remember DOWN event ALWAYS as this is importnt start for every gesture switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(LOG_TAG, "down event : " + Float.toString(mStartNativeY)); break; case MotionEvent.ACTION_MOVE: movY = (int) ((int) mStartNativeY - event.getY()); mWebView.scrollBy(0, movY); Log.d(LOG_TAG, "move event : " + Integer.toString(movY)); return true; case MotionEvent.ACTION_UP: Log.d(LOG_TAG, "up event : " + Integer.toString(mWebView.getScrollY())); return false; default: break; } return true; } // GestureDetector Listener @Override public void onLongPress(MotionEvent e) { } @Override public boolean onDown(MotionEvent e) { Log.d(LOG_TAG, "onDown"); return true; // else won't work } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float velocitX, float veloctiyY){ return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { new Flinger().start((int)velocityY); invalidate(); return true; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } private class Flinger implements Runnable { private final Scroller scroller; private int lastY = 0; Flinger() { scroller = new Scroller(getContext()); } void start(int initialVelocity) { int initialY = mWebView.getScrollY(); int maxY = Integer.MAX_VALUE; // or some appropriate max value in your code scroller.fling(0, initialY, 0, initialVelocity, 0, 10, 0, maxY); Log.i(LOG_TAG, "starting fling at " + initialY + ", velocity is " + initialVelocity + ""); lastY = initialY; mWebView.post(this); } public void run() { if (scroller.isFinished()) { Log.i(LOG_TAG, "scroller is finished, done with fling"); return; } boolean more = scroller.computeScrollOffset(); int y = scroller.getCurrY(); int diff = lastY - y; Log.d(LOG_TAG, "finger run : lasty : " + lastY +" y: " + y + " diff: "+Integer.toString(diff)); if (diff != 0) { mWebView.scrollTo(0, scroller.getCurrY()); lastY = y; } if (more) { mWebView.post(this); } } boolean isFlinging() { return !scroller.isFinished(); } void forceFinished() { if (!scroller.isFinished()) { scroller.forceFinished(true); } } } 

    Problema: la fuga non funziona mai come dovrebbe. quindi se inizio la fuga con una velocità di 1009, il log dice:

     starting fling at 3032, velocity is 1009 up event : 3032 finger run : lasty : 3032 y: 3047 diff: -15 finger run : lasty : 3047 y: 3063 diff: -16 finger run : lasty : 3063 y: 3078 diff: -15 finger run : lasty : 3078 y: 3090 diff: -12 finger run : lasty : 3090 y: 3102 diff: -12 finger run : lasty : 3102 y: 3106 diff: -4 finger run : lasty : 3106 y: 3110 diff: -4 finger run : lasty : 3110 y: 3113 diff: -3 finger run : lasty : 3113 y: 3116 diff: -3 finger run : lasty : 3116 y: 3118 diff: -2 finger run : lasty : 3118 y: 3119 diff: -1 finger run : lasty : 3119 y: 3120 diff: -1 

    Secondo il registro, la teoria della fuga sta funzionando ma il punto di partenza del rotolo (-15) non è sufficiente, dovrebbe essere più di ~ 100


    EDIT: Soluzione alternativa: evento di spedizione a WebView

    Un'altra soluzione spiegata nel commento dovrebbe essere quella di submit il MotionEvent dal FrameLayout al suo fratello WebView con

     mFrameLayout.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouchEvent(MotionEvent ev) { return mWebView.onTouchEvent(MotionEvent ev); } }); 

    Il problema con questa soluzione è che la distanza di scorrimento non è la stessa del WebView onTouchEvent predefinito. È troppo lento: la distanza in cui è scorrere è inferiore a quella che dovrebbe essere e qualche flicker accadere.


    Ogni consiglio o altra soluzione è presa.

  • mailto: links che non aprono l'applicazione di posta su Android in cordova app
  • Come si imposta le date min e max in un Android DatePicker?
  • Visualizzare l'ora e la data corrente in un'applicazione Android
  • Utilizza l'API di Direzione GoogleMap o MapBox per implementare la mia navigazione nella mia app
  • Filtrare le righe da Cursore in modo che non si visualizzino in ListView
  • Imansible get Listview per aggiornare dal database
  • One Solution collect form web for “Spedire l'evento di touch da una vista al suo fratello WebView”

    Ogni vista ha il suo metodo onTouchEvent, che può essere chiamato in modo indipendente.

    Quindi, per passare un evento di touch alla sbilanciatura, dobbiamo solo call onTouchEvent della sifilazione con il parametro MotionEvent di prima vista.

     yourView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouchEvent(MotionEvent ev) { //a sibiling MUST be final to be used in an OnTouchListener //if it's not, create a new final reference to it return sibiling.onTouchEvent(MotionEvent ev); } }); 

    AGGIORNARE:

    Secondo la conversazione nei commenti, si desidera sincronizzare i rotoli di due viste diverse di diverse size. Essere di size diverse, semplicemente passando MotionEvent al MotionEvent non è sufficiente. But è ansible modificare la coordinata X e Y di MotionEvent prima di inviarla a una sifilatura, utilizzando questo metodo:

     ev.setLocation(float x, float y); 

    Sarà necessario scalare X e Y del frammento in base alle size del frammento e del WebView. Non so quale tipo di scorrimento abbia così bisogno di scalare proprio , quindi l'algorithm matematico può essere diverso da quello che ho scritto qui di seguito (ma questo è comunque a te). Se questo è il caso, considerate questa una dimostrazione su come scalare il MotionEvent .

     //you first need all dimensions //if they're changing during runtime, you can do all this in OnTouchListener final int fragWidth = fragment.getWidth(); final int fragHeight = fragment.getHeight(); final int webWidith = webView.getWidth(); final int webHeight = webView.getHeight(); //then you calculate scale factors final float scaleX = (float)webWidth / (float)fragWidth; final float scaleY = (float)webHeight / (float)fragHeight; //same old OnTouchListener fragment.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouchEvent(MotionEvent ev) { //calculate scaled coordinates float newX = ev.getX() * scaleX; float newY = ev.getY() * scaleY; //MODIFY the MotionEvent by setting the scaled coordinates ev.setLocation(newX, newY); //call WebView's onTouchEvent with the modified MotionEvent return webView.onTouchEvent(MotionEvent ev); } }); 
    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.