Android学习之View的事件体系完全解析

  本文尝试着从View的一些基本概念,view的滑动,以及令人头疼的滑动冲突等来解析一下View.

什么是View

  我们都知道Activity在Android中承担着可视化的功能,而显示的往往就是各种控件的组合,例如Button,TextView,甚至是复杂的ListVIew……而这些都是View.而我们所用的布局LinearLayout,RelativeLayout等就是一组View,也就是ViewGroup,但他们的本质还是一个View.View是一个抽象类.

View的滑动

  在讲View的滑动之前,有必要先讲一下MotionEvent和TouchSlop等几个概念:

MotionEvent

  在手指触摸屏幕后产生的事件中,最常用也就三个:
  ACTION_DOWN:手指刚碰到屏幕
  ACTION_MOVE:手指已经开始移动(移动的时候一直触发事件)
  ACTION_UP:手指从屏幕上松开
  而每一次触摸屏幕时都会触发相关事件,
  只是点击屏幕然后松开:DOWN —> UP,
  滑动一会然后松开:DOWN —> MOVE—> MOVE —> MOVE —> MOVE…… —>UP,
  同时,我们可以通过系统提供的两组方法来获取相关坐标:
  getX/getY:返回的是触摸部位相对于当前View左上角的坐标,
  getRawX/getRawY:返回的是触摸部位相对于屏幕左上角的坐标,

TouchSlop

是一个系统默认滑动距离的常量,当你所滑动距离小于这个距离的时候,就会认为你没有做滑动操作,可通过:ViewConfiguration.get(getContext()).getScaledTouchSlop来获取.,默认是8dp.

VelocityTracker

速度追踪,用于追踪手指在滑动过程中的速度,包括水平和垂直方向的,一般是在View的onTouchEvent()中使用:

//获取当前事件的速度
VelocityTracker velocityTracker = VelocityTracker.ontain();
velocityTracker.addMovement(event);//添加追踪事件
velocityTracker.computeCurrentVelocity(1000);//计算速度,一段时间内滑动的像素
int xVelocity = velocityTracker.getXVelocity();//获取水平速度
int yVelocity = velocityTracker.getyVelocity();//获取垂直速度
velocityTracker.();//清除
velocityTracker.recycle();//回收

以上是VelocityTracker的基本使用流程.

GestureDetector

手势检测器,包括检测用户的单击,滑动,长按,双击等.使用过程:
首先是创建GestureDetector对象,并实现OnGestureListener接口,

//要想让手势是识别器生效,必须将手势识别器注册到屏幕的触摸事件中
GestureDetector gestureDetector = new GestureDetector(this);
//接着,接管目标View的onTouchEvent()方法,
public boolean onTouchEvent(MotionEvent event) {
    gestureDetector.onTouchEvent(event);
    return super.onTouchEvent(event);
}

此外,GestureDetector并不是必须使用的,我们完全可以自己在目标View中实现相关滑动的判断.

Scroller

弹性滑动对象.用于实现View的弹性滑动,但它本身无法让VIew弹性滑动,需要结合View的computeScroller()配合来实现这个功能.

Scroller scroller = new Scroller(mContext);
//缓慢滑动到指定位置
private void smoothScrollerTo(int destX, int destY){
    int scrollerX = getScrollerX();
    int delta = destX-scrollerX;
    //1000ms内慢慢滑向destX
    scroller.startScroll(scrollerX,0,delta,1000);
    invalidate();//申请重绘界面
}
@Override
private void computeScroller(){
    if(scroller.computeScrollOffset()){
        scrollTo(scroller.getCurrX(),scroller.getCurrY());
        postInvalidate();
}

}

  接下来开始正式讲View的滑动,有了前面的一点小概念,相信理解起来不会很枯燥.实现滑动主要有三种方法:使用View本身提供的scrollTo/scrollBy方法来实现滑动,通过动画给VIew施加平移实现滑动,改变View的layoutParams使得View重新布局从而实现滑动.

使用scrollTo/scrollBy

  为了实现滑动,View提供来专门的方法来实现这个功能,其中scrollBy本质上也是调用scrollTo来实现的,它实现了基于当前位置的相对滑动,而scrollTo实现了基于所传递参数的绝对滑动.但它们均只能改变View位置的内容,而不能改变View在布局中的位置
  其中需要注意的是View内部的两个属性mScrollX和mScrollY,前面也说到了,可以分别使用getScrollerX和getScrollerY来得到.mScrollX指的是View的左边缘和View内容在左边缘的距离,mScrollY指的是View的上边缘和View内容在上边缘的距离.

使用动画

通过动画我们可以让一个View进行一个平移,平移本身就是一种滑动.实现也比较简单,主要是操作translationX和translationY属性.

//属性动画实现,改变的是View的位置
ObjectAnimator.ofFloat(targetView,"translationX",0,100).setDuration(100).start();

当然还可以用View的补间动画来实现.不过,我们需要知道的是:补间动画平移的也只是内容,而View的布局属性仍然是不变的,如果是想实现点击事件,那是无法完成的,而点击原来的位置又还能相应事件.

改变布局参数

就是改变LayoutParams,,这是比较灵活,也是比较简单的,直接获取控件的LayoutParams,然后设置变化的距离即可.

  前面所讲的三种滑动方式只是比较生硬地滑动过去,为了良好的用户体验,我们最好使用弹性滑动.

View的弹性滑动

  本质是:将一次大的滑动分成若干次小的滑动并在一定的时间内完成,就像静态画面向动态画面的过度,也就是渐进式.实现的方式有:前面说到的Scroller结合computeScroller,通过动画,通过延时机制等等

使用Scroller

前面也说了,也给了简单的demo:

Scroller scroller = new Scroller(mContext);
//缓慢滑动到指定位置
private void smoothScrollerTo(int destX, int destY){
    int scrollerX = getScrollerX();
    int delta = destX-scrollerX;
    //1000ms内慢慢滑向destX
    scroller.startScroll(scrollerX,0,delta,1000);
    invalidate();//申请重绘界面
}
@Override
private void computeScroller(){
    if(scroller.computeScrollOffset()){
        scrollTo(scroller.getCurrX(),scroller.getCurrY());
        postInvalidate();//再次申请重绘
}

}

  那么这段代码是如何实现弹性滑动的呢?
  其实关键是invalidate(),该方法会请求重绘界面,在View的draw方法中会调用computeScroller(),而computeScroller()内部又通过postInvalidate()申请重绘界面,同样还是会调用computeScroller()……想想,绘制的内容是一样,而且是在一定时间内重复这个动作的,那肯定是会形成一种动画效果的.者也是符合我们前面所说的那个本质的:将一次大的滑动分成若干次小的滑动并在一定的时间内完成

通过动画

动画本来就是一种渐进的方式实习那滑动的.这个这里不多讲,动画也是Android中比较重要的,需要一定的篇幅来讲.大家有兴趣可以去看看相关的博客.

使用延时策略

核心是通过发送一系列的延时信息从而达到一种渐近式的方式,使滑动具有渐进式.还是比较简单的,无非是通过Handler或postDelayed().

事件分发机制

  所谓事件分发,只的也就是MotionEvent事件的分发,主要涉及三个方法:
  单个View需要重写onTouchEvent()dispatchEvent()方法,ViewGroup需要多重写一个onInterceptTouchEvent()(拦截事件的核心)

dispatchTouchEvent:
触摸控件(View)首先执行dispatchTouchEvent方法。而在dispatchTouchEvent方法中先执行onTouch方法,后执行onClick方法(onClick方法在onTouchEvent中执行)。

onTouchEvent:
onTouchEvent方法中会在ACTION_UP分支中触发onClick的监听。

onInterceptTouchEvent:用来判断是否拦截某个事件,如果当前VIew拦截了某个事件,那么在同一个事件序列中,此方法不会再被调用.

  我们可以设想一个场景来理解事件分发机制:假设ViewGroupA,ViewGroupB(在ViewGroupA中)和View分别代表公司里的总经理,部门经理和苦逼的你.那么有一个大客户(点击事件)有个需求,那么任务先经过总经理(ViewGroupA)分发下来(dispatchTouchEventA),总经理觉得这个任务要让下属来完成,那么不拦截(onInterceptTouchEventA返回false),那么任务需求分发(dispatchTouchEventB)到了部门经理(ViewGroupB)中,他也觉得任务需要手下来完成(onInterceptTouchEventB返回false),那么任务需求分发(dispatchTouchEventC)到了苦逼的你的手中,既然收人钱财,当然要干活啊(onTouchEventC),活干完了就要提交任务给部门经理,让部门经理来处理(onTouchEventB),部门经理完成后,觉得还行就提交给总经理,让他来处理(onTouchEventA),那么一次完整的任务就完成了...

分发过程:Activity–phoneWindow–DecorView–MyView
事件处理:onTouch(OnTouchListener)–onTouchEvent–onClick(OnClickListener)

这个View事件到此就分析完毕,还有很多细节的东西,大家最好还是自己去看看,接下来会讲一下View的滑动冲突...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值