【问题标题】:MotionEvent handling in ScrollView in AndroidAndroid 中 ScrollView 中的 MotionEvent 处理
【发布时间】:2012-10-04 18:08:03
【问题描述】:

我一直试图弄清楚 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

我查看了the source code of the ScrollView class,它覆盖的唯一与触摸相关的方法是我自己覆盖的方法。所以我不明白 LinearLayout 和 ScrollView 的行为有何不同。

【问题讨论】:

    标签: android touch scrollview motionevent


    【解决方案1】:

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

    概述

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

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

    ScrollView 拦截 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消费。

    希望这会有所帮助。

    问候。

    【讨论】:

    • 问题与触摸事件无关,而是与 ScrollView 和 LinearLayout 的 LayoutParams 的差异有关:FILL_PARENT 在 ScrollView 中没有意义,而是我不得不将 SetFillViewport 设置为 true :) 无论如何,谢谢。
    • 我读过的关于 onInterceptTouchEvent() 和 onTouchEvent() 的最佳解释。非常感谢!
    【解决方案2】:

    我在滚动视图上有带有动画的视图。动画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);
    }
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多