5

我一直试图弄清楚 Android 中 ScrollViews 中 MotionEvents 的行为,但有些东西我无法弄清楚。

作为一个例子,我制作了一个 Activity,里面有一个 ScrollView,而 ScrollView 里面有一个 LinearLayout。我实现了自己的类来控制与触摸相关的功能:

    public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyInnerLayout inner = new MyInnerLayout(getApplicationContext());
        MyLayout layout = new MyLayout(getApplicationContext());

        layout.addView(inner,new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
        setContentView(layout);

    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("scrollview","activity dispatchTouchEvent "+ev.getAction());
        return super.dispatchTouchEvent(ev);
    };

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        Log.i("scrollview","activity on touch "+ev.getAction());
        return super.onTouchEvent(ev);
    }




    public class MyLayout extends ScrollView {

        public MyLayout(Context context) {
            super(context);
        }

        @Override
        public boolean dispatchKeyEvent(KeyEvent ev) {
            Log.i("scrollview","layout dispatchKeyEvent "+ev.getAction());
            return super.dispatchKeyEvent(ev);
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            Log.i("scrollview","layout onInterceptTouchEvent "+ev.getAction());
            return false;
        }

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            Log.i("scrollview","layout on touch "+ev.getAction());
            return false;
        }

    }

    public class MyInnerLayout extends LinearLayout{

        public MyInnerLayout(Context context) {
            super(context);
        }

        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.i("scrollview","inner layout dispatchTouchEvent "+ev.getAction());
            return true;
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            Log.i("scrollview","inner layout onInterceptTouchEvent "+ev.getAction());
            return true;
        }

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            Log.i("scrollview","inner layout on touch "+ev.getAction());
            return true;
        }

    }

}

当我单击屏幕上的任意位置时,我会收到以下日志:

10-14 18:11:48.631: I/scrollview(14906): activity dispatchTouchEvent 0
10-14 18:11:48.631: I/scrollview(14906): layout onInterceptTouchEvent 0
10-14 18:11:48.631: I/scrollview(14906): layout on touch 0
10-14 18:11:48.631: I/scrollview(14906): activity on touch 0
10-14 18:11:48.647: I/scrollview(14906): activity dispatchTouchEvent 1
10-14 18:11:48.647: I/scrollview(14906): activity on touch 1

这意味着触摸事件没有进入滚动视图内的内部布局。但是,当我将 ScrollView 更改为 LinearLayout 时(只需在扩展中更改它),事件就会下降到内部布局:

10-14 18:24:08.975: I/scrollview(15115): activity dispatchTouchEvent 0
10-14 18:24:08.975: I/scrollview(15115): layout onInterceptTouchEvent 0
10-14 18:24:08.975: I/scrollview(15115): inner layout dispatchTouchEvent 0
10-14 18:24:09.045: I/scrollview(15115): activity dispatchTouchEvent 1
10-14 18:24:09.045: I/scrollview(15115): layout onInterceptTouchEvent 1
10-14 18:24:09.045: I/scrollview(15115): inner layout dispatchTouchEvent 1

我查看了 ScrollView 类的源代码,它覆盖的唯一与触摸相关的方法是我自己覆盖的方法。所以我不明白 LinearLayout 和 ScrollView 的行为有什么区别。

4

2 回答 2

10

也许您已经弄清楚了上述行为的原因,但以防万一,原因如下。

概述

onInterceptTouchEvent()被称为自上而下(从父级到子级),使一个视图能够在被子级处理之前拦截运动事件。

它们被onTouchEvent()称为自下而上(从子级到父级),直到其中一个消耗它并且循环结束。

AScrollView拦截MotionEvent以检查它是否应该在将视图传递给孩子之前滚动视图。如果应该执行滚动,则消耗事件并且子视图什么也看不到。

在 的情况下LinearLayout,没有理由在 期间消耗事件onInterceptTouchEvent(),并​​且总是传递给子视图。

您的代码中发生了什么

因为MyInnerLayout是空的ScrollView,总是在消耗MotionEvent

例如,如果您像这样设置内部布局背景:

    MyInnerLayout inner = new MyInnerLayout(getApplicationContext());
    inner.setBackground(getResources().getDrawable(R.drawable.ic_launcher));
    MyLayout layout = new MyLayout(getApplicationContext());

您会看到,如果您触摸背景图像,该事件将传递给孩子。如果您在背景图像之外触摸,该事件将被ScrollView.

希望这可以帮助。

问候。

于 2012-11-29T15:06:35.273 回答
0

我在滚动视图上有带有动画的视图。动画strat 和stop 等于运动事件(DOWN 和UP)。我修复了相同的解决方案:

public class RootActivity extends Activity implements OnTouchListener {

private View tochedView = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_root); 

    ((View) findViewById(R.id.btnOther)).setOnTouchListener(this);

    ScrollView scroll = (ScrollView) findViewById(R.id.scrollView);
    scroll.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_UP && tochedView != null) {
                Log.i("Touche", "ScrollView ACTION_UP");
                Animation upAnim = AnimationUtils.loadAnimation(RootActivity.this, R.anim.btn_up_anim);
                tochedView.startAnimation(upAnim);
                tochedView = null;
                return true;
            }
            return false;
        }
    });
}


private void animateView(View v, MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        Log.i("Touche", "ACTION_DOWN");
        Animation downAnim = AnimationUtils.loadAnimation(this, R.anim.btn_down_anim);
        v.startAnimation(downAnim);
        tochedView = v;
    }
    if (event.getAction() == MotionEvent.ACTION_UP && tochedView != null) {
        Log.i("Touche", "ACTION_UP");
        Animation upAnim = AnimationUtils.loadAnimation(this, R.anim.btn_up_anim);
        v.startAnimation(upAnim);
        tochedView = null;
    }

}


@Override
public boolean onTouch(View v, MotionEvent event) {
        animateView(v, event);
}

}

于 2014-04-08T16:15:09.657 回答