android(仿QQ向右滑动退出)在viewpager中onTouchEvent无法监听到ACTION_DOWN的getX的值


    最近笔者在做项目的时候遇到一个问题,implements OnTouchListener中重写onTouch事件无法获取ACTION_DOWN中getX的值


    笔者想实现的效果:一个A页面,一个B页面(包含4个fragment的viewpager),viewpager可以实现fragment横滑切换,当fragment为0也就是第一个页面时,通过ontouch接口,在ACTION_DOWN获取用户点击屏幕的X距离StartX,再通过action_move获取用户滑动的距离SlipX,通过当StartX-SlipX>100,实现finish页面。再通过overridePendingTransition(int enterAnim, intexitAnim)设置进入,退出的动画,实现仿QQ横滑退出的效果。

;android(仿QQ向右滑动退出)在viewpager中onTouchEvent无法监听到ACTION_DOWN的getX的值_第1张图片


    问题描述:在ViewPager绑定onTouch事件中的ACTION_DOWN中无法获取getX的距离,然而action_move,action_up中确能够获取到getX的值。


    好了,想必大家也明白了想要达到的效果以及遇到的问题,那么笔者也不废话了。直接说明出现此问题的原因,以及解决方案,最后当然是源码共享。


    核心原因:对于(dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent),以及onTouch这几个方法没有完全搞明白,以及其传递机制。于是本着严谨的态度,通过万能的互联网终于搞明白了这几个之间的关系,以及用法,下面这个图很好的说明了该问题。(后面我会贴出相关参考的帖子链接,大家有兴趣可以看一下).

     1.首先onTouch方法是继承OnTouchListener接口需要重写的方法,当一个View绑定了该监听的时候,就会调用onTouch方法来监听用户对该View的手指操作.(什么是View?也就是一般的源生控件.例如button,textview,以本案例的viewpager都属于view或者自定义View).

mViewPager.setOnTouchListener(this);// 为ViewPager设置ontouch监听获取滚动距离

      在这种情况下,如果我们在Activity里面为一个View控件绑定了setOnTouchListener,那么当屏幕有touch事件的时候,首先会是绑定该监听的View响应onTouch事件,执行onTouch方法.

      如果onTouch返回值为true,表示这个touch事件被onTouch方法处理完毕,不会把touch事件再传递给Activity,也就是说activity的dispatchTouchEvent方法不会被调用,在本案例中,也就是viewpager就不会滑动了(有兴趣可以试一下)。

     如果onTouch的返回值是false,表示这个touch事件没有被完全处理,onTouch返回以后,touch事件被传递给Activity,activity的dispatchTouchEvent方法被调用.

     2.其次dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent,如下图所示,简单解释一下吧.

         *activity里可以重写dispatchTouchEvent,onTouchEvent两个方法,实现监听.

        *viewgroup里可以重写dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三个方法,实现监听.(什么是ViewGroup?也就是一般的放置View的容器,一般是代码自定义button,textview这些)

          *view里面可以重写dispatchTouchEvent,onTouchEvent两个方法,实现监听.

     3.接下来,就是其传递机制如下图所示

      Android中默认情况下事件传递是由最终的view的接收到,传递过程是从父布局到子布局,也就是从Activity到ViewGroup到View的过程,从上向下分发.

     (可能这里有些同学觉得有点绕,举个例子,如果在一个activity里面都实现了Activity\ViewGroup\的dispatchTouchEvent方法以及View的Ontouch方法,那么当用户点击屏幕的时候,获取到用户action_down的getX控制台打印顺序,依次是Activity→ViewGroup→View)

       (1) 首先会去触发activity里面的dispatchTouchEvent事件

       (2)然后是ViewGroupdispatchTouchEvent事件(一般是代码自定义控件的时候重写该方法才会去执行)

       (3)  最后是ViewdispatchTouchEvent事件.因为是为View,setOnTouchListener.所以会去执行onTouch方法.


         因此这个问题就迎刃而解了,如果说我在onTouch里面无法获取到ACTION_DOWN的getX的值,那么我就重写activity的dispatchTouchEvent方法,在这里直接获取ACTION_DOWN的getX的值,然后在将touch事件传递下去在ontouch获取ACTION_MOVE的getX的值,那么我就能通过当StartX-SlipX>100,实现finish页面了.


        不过要注意的地方有两点:

        1.当一个touch事件执行完毕,最后return的true;表示这个touch事件处理完毕,不会把touch事件再向下传递.反之,false,则向下传递

        2.  在重写dispatchTouchEvent等方法的时候,一定要 super.dispatchTouchEvent(ev);否则事件会就此打住,任凭你如何滑动,屏幕也会一定不动

android(仿QQ向右滑动退出)在viewpager中onTouchEvent无法监听到ACTION_DOWN的getX的值_第2张图片

  最后这里直接上核心代码。

/**
 * 带有4个fragment的ViewPager,并且在第一个fragment的时候实现向右滑动finish当前页面返回上一个页面
 * @author max
 */
public class SecondActivity extends FragmentActivity implements
		OnClickListener, OnTouchListener, OnPageChangeListener {
	private ViewPager mViewPager;// 滑动的viewpager
	private TextView text1, text2, text3, text4;// 点击跳转的fragment页面
	private List<Fragment> mTabs = new ArrayList<Fragment>(); // fragment集合
	private FragmentPagerAdapter mAdapter; // viewpager的adapter
	// 记录开始手指点击的位置,和滑动的X距离
	private int StartX, SlipX;
	// 当前的页面
	private int currentItemNum = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.second_activity);
		init();
		initDatas();
		mViewPager.setAdapter(mAdapter);
		mViewPager.setOnPageChangeListener(this);// 设置页面滚动监听获取当前页面的页码
		mViewPager.setOnTouchListener(this);// 为ViewPager设置ontouch监听获取滚动距离
	}

	/** 位fragment的list传值 **/
	private void initDatas() {
		Fragment1 fragment1 = new Fragment1();
		Fragment2 fragment2 = new Fragment2();
		Fragment3 fragment3 = new Fragment3();
		Fragment4 fragment4 = new Fragment4();
		// 往list加入4个碎片
		mTabs.add(fragment1);
		mTabs.add(fragment2);
		mTabs.add(fragment3);
		mTabs.add(fragment4);
		// 初始化适配器
		mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {

			@Override
			public int getCount() {
				return mTabs.size();
			}

			@Override
			public Fragment getItem(int arg0) {
				return mTabs.get(arg0);
			}
		};
	}

	/** 初始化控件 **/
	private void init() {
		mViewPager = (ViewPager) findViewById(R.id.viewpage);
		text1 = (TextView) findViewById(R.id.text1);
		text2 = (TextView) findViewById(R.id.text2);
		text3 = (TextView) findViewById(R.id.text3);
		text4 = (TextView) findViewById(R.id.text4);
		text1.setOnClickListener(this);
		text2.setOnClickListener(this);
		text3.setOnClickListener(this);
		text4.setOnClickListener(this);
	}

	/** 点击text切换子页面 **/
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.text1:
			mViewPager.setCurrentItem(0);
			break;
		case R.id.text2:
			mViewPager.setCurrentItem(1);
			break;
		case R.id.text3:
			mViewPager.setCurrentItem(2);
			break;
		case R.id.text4:
			mViewPager.setCurrentItem(3);
			break;
		default:
			break;
		}
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		// 一定要spuer,否则事件打住,不会在向下调用了
		super.dispatchTouchEvent(ev);
		switch (ev.getAction()) {
		// 记录用户手指点击的位置
		case MotionEvent.ACTION_DOWN:
			StartX = (int) ev.getX();
			Log.i("info", "StartX = " + StartX);
			break;
		}
		return true;// return false,继续向下传递,return true;拦截,不向下传递
	}

	// 默认是重写onTouch事件
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			StartX = (int) event.getX();
			Log.i("info", "StartX11111 = " + StartX);
			break;
		case MotionEvent.ACTION_MOVE:
			// 获取滑动时候的X距离
			SlipX = (int) event.getX();
			Log.i("info", "1= " + SlipX);
			if (currentItemNum == 0 && SlipX - StartX > 100) {
				// 在第一个页面,手势向右滑动,finish当前页面
				finish();
				// 设置切换切换动画
				overridePendingTransition(R.anim.slide_left_in,
						R.anim.slide_right_out);
			}
			break;

		default:
			break;
		}
		return false;
	}

	@Override
	public void onPageScrollStateChanged(int arg0) {

	}

	@Override
	public void onPageScrolled(int arg0, float arg1, int arg2) {

	}

	@Override
	public void onPageSelected(int arg0) {
		// 获取当前页码
		currentItemNum = arg0;
	}
}


最后当然是源码,仿QQ横滑finish当前 activity,带做右进左出的动画.点击下载

第一次发布博客,稍微有点紧张,如有有纰漏,欢迎各位看官指出,大家一起相互学习

最后感谢万能的互联网,以及各位勤劳的博主,以下贴出参考资料地址,有兴趣的童鞋可以去看看.

http://blog.csdn.net/yanzi1225627/article/details/22592831    (打印出了各事件点击打印日志)

http://blog.csdn.net/xyz_lmn/article/details/12517911 (ontouch事件分发机制图片详解)

你可能感兴趣的:(android(仿QQ向右滑动退出)在viewpager中onTouchEvent无法监听到ACTION_DOWN的getX的值)