从视图调度触摸事件给他的WebView兄弟 [英] Dispatch touch event from a view to his WebView sibling

查看:372
本文介绍了从视图调度触摸事件给他的WebView兄弟的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题的简历:在布局自己的WebView兄弟实现比默认的WebView滚动(与一扔)相同的滚动

Resume of the problem : dispatch touch event on a layout to his WebView sibling achieving the same scroll than the default WebView scroll (with fling)

我有以下这个XML一个的FrameLayout通过一个web视图:

I have a frameLayout over an WebView following this xml :

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

在应用程序的开头,一个空的HTML占位符某处的WebView大教堂放置和他的位置由JavascriptInterface给出。这个位置被转换以像素比率和的FrameLayout被放置在的FrameLayout上方。
当视图内容移动,移动占位符的所以用的FrameLayout(事件从可观察的WebView sended)。到目前为止,一切工作正常,因为它应该。

During the begining of the app, an empty html placeholder is placed somewhere in the WebView Dom and his position is given by an JavascriptInterface. This position is converted with pixel ratio and the FrameLayout is placed above the frameLayout. When the WebView content moved, the placeholder moved an so with the FrameLayout (event sended from the Observable WebView). So far, everything is working as it should.

在我的FrameLayout需要听就可以了触摸点击,所以我已经设置好的一个TouchListener下面这一步:

In the frameLayout I need to listen a touch click on it so I've setted a TouchListener following this step :

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

触摸事件到达正确的监听器和一切工作正常。除了当我滚动的FrameLayout,因为它是的FrameLayout的兄弟web视图不滚动:它不是一个父/子

显而易见的解决方案将设置correclty返回真/假的View.onTouchEvent或听者因此Android调度事件在三个Latout树下一个视图。但是,因为我需要处理倒在的FrameLayout /事件,开始为 MotionEvent.ACTION_DOWN 返回true停止调度下一个事件。

The obvious solution will be to set correclty return true/false for View.onTouchEvent or in the listener so Android dispatch the event to the next view in three Latout Tree. But because I need to handle down/up event in the FrameLayout, starting to return true for MotionEvent.ACTION_DOWN stop dispatching the next event.

在上周搜索在计算器上,我已经实现了解决方案,在50%的工作。让我们来形容它:

Searching on StackOverFlow during the last week, I've achieve a solution working at 50%. Let's describe it :

该解决方案包括拦截上的FrameLayout事件,并根据设定的涡旋运动/事件的web视图的滚动位置的。在问题与此解决方案是滚动的一扔事件没有被管理(一扔:当用户刷卡,并从屏幕上删除他的手指它产生的惯性作用继续滚动向前冲一些毫秒)。加入抛在步骤2进行说明。

The solution consist of intercepting event on the FrameLayout and setting the scroll position of the WebView according the the scroll movement/event. The issue with this solution is that the fling event of the scroll is not managed (fling : when user swipe and remove his finger from the screen it produced an inertia effect of the scroll continuing to move for some ms). Adding the fling is explained in step 2.

这是code段:(为的FrameLayout自定义视图部分)

This is the code snippet : (part for the FrameLayout custom view)

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


步骤2:一扔

添加一扔到狲scrollBy也不是那么容易。基本是增加一个GestureDetector和一个滚轮。所以我一类implementaing Runnable的一个滚轮它管理一扔效果。此类呼吁GestureDetector监听的onFling。


Step 2 : the fling

Adding the fling to the manul scrollBy is not so easy. The basic is to add a GestureDetector and a Scroller. So I've a class implementaing Runnable with a Scroller which managing the fling effect. This class is called on the onFling of the GestureDetector 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 important 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);
        }
    }
}

问题:一扔不是因为它应该每次工作。所以如果我开始的1009速度一扔,日志说:

Issue : the fling is not working everytime as it should. so if I start the fling with a velocity of 1009, the log says :

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 

根据日志,一扔理论工作,但滚动(-15)的出发点是不够的,它应该比100〜

According to the log, the fling theory is working but the starting point of scroll (-15) is not enough, it should be more than ~100

另一种解决方案在评论解释应该是从的FrameLayout到他的WebView兄弟以

Another solution explained in comment should be to send the MotionEvent from the FrameLayout to the his WebView sibling with

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

这个解决方案的问题在于,滚动距离的不可以同为默认的WebView onTouchEvent。这是太慢了:当滚动距离小于它应该是什么,有些闪烁发生。

The problem with this solution is that the scroll distance is not the same as the default WebView onTouchEvent. It's too slow : the distance when it scroll is less than what it should be and some flicker happen.

任何意见或其他解决办法是采取。

推荐答案

每个视图都有onTouchEvent方法,可以独立调用。

Every view has its onTouchEvent method, that can be called independently.

所以,为了一个触摸事件传递给sibiling,我们只需要调用sibiling的onTouchEvent与第一种观点的MotionEvent参数。

So, in order to pass a touch event to the sibiling, we just need to call the sibiling's onTouchEvent with the first view's MotionEvent parameter.

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

更新:

根据意见交谈中,要同步的不同尺寸的2种不同的看法滚动。作为不同尺寸的,只是传递 MotionEvent 来web视图是不够的。 但是您可以修改MotionEvent的X和其发送到sibiling,使用此方法之前Y坐标:

According to the conversation in comments, you want to synchronize scrolls of 2 different views of different dimensions. Being of different dimensions, simply passing MotionEvent to the webView is not enough. But you can modify the MotionEvent's X and Y coordinate before sending it to a sibiling, using this method:

ev.setLocation(float x, float y);

您需要根据您的片段和你的WebView的尺寸缩放X和片段的年。我不知道是什么类型的滚动缩放这样您的precisely 需要,所以数学算法可以从我下面写的不同(但无论如何这是由你)。如果是这样的话,认为这对如何扩大示威活动的 MotionEvent

You'll need to scale X and Y of Fragment according to the dimensions of your Fragment and your WebView. I don't know what type of scrolling thus scaling you precisely need, so mathematical algorithm may be different from what I wrote below (but that's up to you anyway). If that's the case, consider this a demonstration on how to scale the 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);
    }
});

这篇关于从视图调度触摸事件给他的WebView兄弟的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆