android MotionEvent的相关的类的介绍

来源:互联网 发布:wind数据库 华东师大 编辑:程序博客网 时间:2024/06/05 10:59

常见的类的介绍

     1 MotionEvent  触摸事件
     2 View  视图的位置信息
     3 ViewConfiguration  View相关设置
     4 VelocityTracker  速度追踪器
     5 Scroller 用来滑动的类

MotionEvent

     Android API http://developer.android.com/reference/android/view/MotionEvent.html
     官方的一些介绍
MotionEvent  形容触摸屏幕产生的事件( 这里用点击事件来代替),这个点击事件的对象包含一些Action 的信息和一系列的坐标值的信息,其中action的信息包含了state改变的信息,比如从move -> up,坐标的信息则包括了一些的位置和其他的属性的信息
     比如说,当用户第一次触摸屏幕的时候,系统就会传递一个motionevent的信息给适当的一个view,这个信息包括一些actioncode为Action_Down, 和一系列的值其中包括可能为:x,y,压力,触摸区域的带下,时间等一些列信息
      一些设备可以报告的多个多个运动轨迹,多触摸屏幕中一个运动轨迹对应一个手指,这时的手指对应的运动轨迹设为pointer,而motionevent包含所有的pointer的信息,即使的这个pointer不再移动从上次传达的信息
     pointer 的数量改变只有当手指down或者up的时候改变,除非这个手势被取消cancle()
     每一个pointer的都有一个ID来标示当down的事件发生后,ACTION_DOWN or ACTION_POINTER_DOWN,这个ID值一直存在的,直到 ACTION_UP or ACTION_POINTER_UP)或者这个手势被取消 (indicated by ACTION_CANCEL).
     motionevent提供的一系列的方法用来查询属性的信息,比如getX(int index),getY(int index),getAxisValue(int axis),getPointId(int indx),getToolType(int index),大多数情况下,通过接受index来得到信息,而不是pointer id,这个index 的值介于0到getpointercount().
    Index 和pointer id的区别多个事件的时候,motionevent的顺序是不确定的(index),但是pointer 的id自始至终都是一样的,只要它还存在,我们可以通过getPointId(int index),获得pointer 的id值,也可以通过findPointIndex(int pointId),来获取对应的index的值。
     为了提高效率,action_move的时候,提供一个批机制,就是处理单个对象包含多个运动的样本,当前的pointer可以使用getX(int index),getY(int index)获取批最前的事件 ,而使用getHistoricalX(int index)和getHistoricalY(int index)来获取批里面最早的其他的事件,举个例子
public boolean onTouchEvent(MotionEvent event) {    printMotinEvent(event);    return true;}void printMotinEvent(MotionEvent ev) {    final int historySize = ev.getHistorySize();    final int pointerCount = ev.getPointerCount();    Log.e("history", "history的数量"+historySize);    for (int h = 0; h < historySize; h++) {        for (int p = 0; p < pointerCount; p++) {            Log.e("history",                    "id:"+ev.getPointerId(p)+ ",2 x:"+ev.getHistoricalX(p, h)+",3 y:"+ev.getHistoricalY(p, h));        }    }    for (int p = 0; p < pointerCount; p++) {        Log.e("now", ev.getEventTime()+"time");        Log.e("now", "1 id:"+ev.getPointerId(p)+",2 x"+ ev.getX(p)+",3 y"+ ev.getY(p));    }}
 
打印的log如下:
B487E909-0CEC-4606-9C53-6799BC4EC2A9
motionevent 即传递,我们触摸屏幕所有的信息!并且一个触摸事件时由多个movetionevent的组成的,
常见的api如下
     event.getAction() or event.getAction() & MotionEvent.ACTION_MASK // 获取触控动作比如 ACTION_DOWN,
     event.getPointerCount() //获取触控点的数量,比如2则可能是两个手指同时按压屏幕
     event.getPoninerId(int index)//对于每个触控的点的细节,我们可以通过一个循环执行getPointerId方法获取索引
     event.getX(int index)//获取第index个触控点的x位置
     event.getY(int index)//获取第index个触控点的y位置
     event.getPressure(int index)// LCD可以感应出用户的手指压力,当然具体的级别由驱动和物理硬件决定的
     event.getDownTime()//按下开始时间
     event.getEventTime()//事件结束时间
     event.getEventTime() - event.getDownTime()//总共按下时花费时间
 

view视图的位置信息

     position的信息:以下四个信息代表相对父容器的位置
          left=getLeft();
          right = getRight();
          up = getUp();
          bottom = getBottom();
     android 3.0 之后的添加的一些新的位置信息
          translationX = getTranslatinX();
          translationY = getTranslationY();
          x = getX()
          y = getY()
     注意前后的关系,left,right,up,bottom四个点的信息在平移的过程是不会发生改变的,translationx和translationy 默任初始为0,三者之前的关系时
getX() = getTranslationX() + getLeft()
getY() = getTranslationY()+getTop();
     代码如下
tv = (TextView) findViewById(R.id.tv);btn = (Button) findViewById(R.id.btn);final ObjectAnimator translationX = ObjectAnimator.ofFloat(tv, "translationX", 0,50);btn.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View v) {        translationX.setDuration(300);        translationX.start();    }});translationX.addListener(new Animator.AnimatorListener() {    @Override    public void onAnimationStart(Animator animation) {        Log.e("TAG","start x = "+tv.getX()+",translationX = " + tv.getTranslationX() +",left = "+tv.getLeft());    }    @Override    public void onAnimationEnd(Animator animation) {        Log.e("TAG","end x = "+tv.getX()+",translationX = " + tv.getTranslationX() +",left = "+tv.getLeft());    }    @Override    public void onAnimationCancel(Animator animation) {    }    @Override    public void onAnimationRepeat(Animator animation) {    }});
 
 打印的log如下
     53B1BE52-CF40-4458-A177-4E2C160520C8

ViewConfiguration

     包含一些方法去获取标准的常量在ui 上,比如一些时间,大小,或者距离,这是android 帮我们设定好的。
     android api
        http://developer.android.com/reference/android/view/ViewConfiguration.html
     ViewConfiguration.get(Context context);
举个常用的例子,自定义一个长按的手势识别器代码如下(需要默认认为最小的滑动的距离和默认最小的进入长按状态的时间)

     LongPressGestureDetector

public class LongPressGestureDetector {    private ViewConfiguration _configuration;    private int _minDistance ;    private int _delay;    private Context _context;    private OnlongPressListener _listener;    private Handler _handler ;    private Runnable _longPressRunnable;    private float _downX;    private float _downY;    private long _downTime;    public LongPressGestureDetector(Context context, OnlongPressListener listener){        _listener = listener;        _context = context;        _handler = new Handler(Looper.getMainLooper());        _configuration = ViewConfiguration.get(context);        _minDistance = _configuration.getScaledTouchSlop();        _delay = _configuration.getLongPressTimeout();        _longPressRunnable = new LongPressRunnale();    }    //这里以单指操作    public boolean onTouch(MotionEvent event){        int actionCode = event.getAction() & MotionEvent.ACTION_MASK;        switch (actionCode){            case MotionEvent.ACTION_DOWN:                _handler.removeCallbacks(_longPressRunnable);                _downX = event.getX();                _downY = event.getY();                _downTime = event.getDownTime();                Log.e("TAG",event.getEventTime()+"downtime"+event.getDownTime());                _handler.postDelayed(_longPressRunnable,_delay);            case MotionEvent.ACTION_MOVE:                int distanceX = (int) Math.abs(event.getX() - _downX);                int distanceY = (int) Math.abs(event.getY() - _downY);                if(distanceY > _minDistance || distanceX > _minDistance ){                    _handler.removeCallbacks(_longPressRunnable);                }                break;            case MotionEvent.ACTION_UP:                if(event.getEventTime() - _downTime < _delay) {                    _handler.removeCallbacks(_longPressRunnable);                }                Log.e("TAG",event.getEventTime()+"downtime"+event.getDownTime());                break;        }        return true;    }    interface OnlongPressListener{        void onLongPress();    }    class LongPressRunnale implements Runnable{        @Override        public void run() {            Log.e("TAG", "进入到了长按的状态了");            if(_listener != null){                _listener.onLongPress();            }        }    }}
这里是以单指作为操作的,如果选择用多指,我们根据每一个pointer的id来判断时否发生改变了,
activity的代码
 
 
public class MainActivity extends AppCompatActivity {    private LongPressGestureDetector _longPressGestureDetector;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        _longPressGestureDetector = new LongPressGestureDetector(this,new LongPressGestureDetector.OnlongPressListener(){            @Override            public void onLongPress() {                Log.e("TAG","开始在主线程更新UI了");            }        });    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return _longPressGestureDetector.onTouch(event);    }}
注意到了这里面在onTouchEvent()全部交给LongPressGestureDetector的去处理,并且在LongPressGestureDetector的ontouch()的方法里面返回了true,这样做其实不好,但是这里只是为了介绍下了ViewConfiguration的这个类,接着长按的进入长按的回调方法中,Log如下:
D5B70B2C-F4C9-4E71-8BFF-DECDA41783D0

VelocityTracker

     android api
http://developer.android.com/reference/android/view/VelocityTracker.html
     用来追踪触摸事件的速度的,主要是为了在filing的等手势的后面的平滑滑动,通过obtain()获取实例,在你想追踪的event的之前使用addmovement(MotionEvent event)去追踪这个event 的,紧接着调用computeCurrentVelocity(int units ),最后就可以使用getXVelocity(int pointerId ),getYVelocity(int pointId)来获取了当前的pointer的在x,y的速度

Scroller

     andorid API
     http://developer.android.com/reference/android/widget/Scroller.html
     这个用于封装了滚动的类,你可以用这个scroller收集你需要制作的动画的一个数据,其实里面只是存储着一些的关于滑动的信息,真正的滑动的实现是通
view中重写的computeScroll()的方法来实现的,
     滑动的实现的原理
          我们知道自定义viewgroup的dispatchdraw的方法中的调用drawChild(),drawChild()的源码如下。
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
也就以为着去调用响应的child的Draw方法。从而完成整体的draw的效果,在view的中的draw的代码中可以找到computeScroll()的代码。
     我们通过在computeScroll()的中使用postInvalidate()从而使整个布局在重绘,从而完成滑动的实现
     结论
          从滑动的原理可以看出来,滑动其实我门的draw的部分,这也就意味着的实现滑动的是内容而不是view的视图的位置,也就是意味着view 的layout的布局信息自始至终都未变过,在滑动的过程中。
常见用法
     public boolean computeScrollOffset ()
     当想要知道新的位置时,调用此函数。如果返回true,表示动画还没有结束。位置改变以提供一个新的位置。
     public void extendDuration (int extend)
     延长滚动动画时间。
     public void fling (int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)
     在fling手势开始滚动。滚动的距离取决于fling的初速度。
     public void startScroll (int startX, int startY, int dx, int dy) 以提供的起始点和将要滑动的距离开始滚动。
     public int timePassed () 返回自滑动开始经过的时间
     利用Scroller和VelocityTracker,我们实现一个可以在fling的时候能够具有滑动惯性。并且在滑动的时候一起跟随滑动,但是没有加外层的限制
     代码如下
          首先上了布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.example.rrtoyewx.person.FlingViewGroup        android:id="@+id/tv"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:background="@android:color/white">        <ImageView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:src="@drawable/image1" />        <ImageView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:src="@drawable/image2" />        <ImageView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:src="@drawable/image3" />        <ImageView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:src="@drawable/image4" />        <ImageView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:src="@drawable/image5" />        <ImageView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:src="@drawable/image6" />        <ImageView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:src="@drawable/image7" />    </com.example.rrtoyewx.person.FlingViewGroup></RelativeLayout>
 
布局很简单就是自定义了viewgroup了,然后里面放了7张图片。

下面的放上了这个自定义viewgroup的实现

package com.example.rrtoyewx.person;import android.content.Context;import android.graphics.Point;import android.graphics.PointF;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.widget.Scroller;/** * Created by Rrtoyewx on 15/11/4. */public class FlingViewGroup extends ViewGroup {    private VelocityTracker _velocityTracker;    private Scroller _scroller;    private ViewConfiguration _viewConfiguration;    private PointF _prePoint;    private PointF _tempPoint;    private PointF _curPoint;    private Point _distancePoint;    private int _touchMinDistance;    private int _maxFlingVelocity;    private int _minFlingVelocity;    public FlingViewGroup(Context context) {        super(context);    }    public FlingViewGroup(Context context, AttributeSet attrs) {        super(context, attrs);        _scroller = new Scroller(context);        _viewConfiguration = ViewConfiguration.get(context);        _touchMinDistance = _viewConfiguration.getScaledTouchSlop();        _maxFlingVelocity = _viewConfiguration.getScaledMaximumFlingVelocity();        _minFlingVelocity = _viewConfiguration.getScaledMinimumFlingVelocity();    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int count = getChildCount();        for (int i = 0; i < count; i++) {            View child = getChildAt(i);            child.layout(l + i * (r - l), t, r + i * (r - l), b);        }    }    //这里以单指来分析即pointerIndex = 0;    @Override    public boolean onTouchEvent(MotionEvent event) {        int actionCode = event.getAction() & MotionEvent.ACTION_MASK;        if(_velocityTracker == null){            _velocityTracker = VelocityTracker.obtain();        }        _velocityTracker.addMovement(event);        switch (actionCode) {            case MotionEvent.ACTION_DOWN:                if (_scroller.isFinished()) {                    _scroller.abortAnimation();                }                _prePoint = new PointF();                _prePoint.x = event.getX(0);                _prePoint.y = event.getY(0);                break;            case MotionEvent.ACTION_MOVE:                calculateMovePoint(event);                _curPoint = _tempPoint;                if (Math.abs(_prePoint.x - _curPoint.x) > _touchMinDistance || (Math.abs(_prePoint.y - _curPoint.y) > _touchMinDistance)) {                    if (_distancePoint != null) {                        _distancePoint = null;                    }                    _distancePoint = new Point();                    _distancePoint.x = (int) (_curPoint.x - _prePoint.x);                    _distancePoint.y = (int) (_curPoint.y - _prePoint.y);                    scrollTo(-_distancePoint.x + getScrollX(), -_distancePoint.y + getScrollY());                    _prePoint = _curPoint;                }                break;            case MotionEvent.ACTION_UP:                int pointId = event.getPointerId(0);                final VelocityTracker velocityTracker = _velocityTracker;                velocityTracker.computeCurrentVelocity(1000, _maxFlingVelocity);                int xVelocity = (int) velocityTracker.getXVelocity(pointId);                int yVelocity = (int) velocityTracker.getYVelocity(pointId);                Log.e("TAG", "xVelocity : " + xVelocity + ",xVelocitY: " + yVelocity);                if(Math.abs(xVelocity)>_minFlingVelocity && Math.abs(yVelocity ) > _minFlingVelocity ){                    _scroller.fling(getScrollX(),getScrollY(), -xVelocity, -yVelocity,Integer.MIN_VALUE,Integer.MAX_VALUE,Integer.MIN_VALUE,Integer.MAX_VALUE);                    awakenScrollBars(_scroller.getDuration());                    invalidate();                }            case MotionEvent.ACTION_CANCEL:                resetState();                break;        }        return true;    }    /**     * 计算event的具体信息     * @param event     */    private void calculateMovePoint(MotionEvent event) {        _tempPoint = new PointF();        int historySize = event.getHistorySize();        for (int h = 0; h < historySize; h++) {            float historicalX = event.getHistoricalX(0, h);            _tempPoint.x += historicalX;            float historicalY = event.getHistoricalY(0, h);            _tempPoint.y += historicalY;        }        _tempPoint.x += event.getX(0);        _tempPoint.y += event.getY(0);        _tempPoint.x /= (historySize + 1);        _tempPoint.y /= (historySize + 1);    }    private void resetState() {        if (_velocityTracker != null) {            _velocityTracker.clear();            _velocityTracker.recycle();            _velocityTracker = null;        }    }    @Override    public void computeScroll() {        super.computeScroll();        if (_scroller.computeScrollOffset()) {            scrollTo(_scroller.getCurrX(), _scroller.getCurrY());            postInvalidate();        }    }    }
 
 
简单的说下的代码的
1 初始化的时候将ViewConfiguration和Scroller,并初始化一些常量,
2  onLayout()一次将孩子放在整个手机屏幕,依次往右放
3  onTouchEvent()中,处理滑动的逻辑,并过滤一些我们不要的motionevent
4  重写computeScroll(),让其能够平滑的滑动
最后附上效果图(模拟器太难滑的了,效果不佳,还请见谅),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1 0