什么是View

View是所有Android中所有控件的基类,不管是Button TextView还是复杂的RelativeLayout和ListView,它们呢的共同基类都是View,View是一种界面层的控件的一种抽象,ViewGroup包含了许多控件,它也继承View,比如自定义TestButton是一个View,它继承了TextView,而TextView也继承了View,所以它们都是View

View的位置参数

View主要由它的4个点来决定,分别是View的4个属性:top left right bottom 这些坐标都是相对View的父容器来说的,它是相对坐标,X轴和Y轴分别为右和下,如下图

所以View的宽高分别是:
width=right-left;height=bottom-top;

如何得到View的4个参数呢?so easy
Left=getLeft();Right=getRight();Top=getTop();Bottom=getBottom();

在Android3.0开始,View增加了额外的几个参数:X、 Y、translationX 、translationY ,X、 Y是View左上角的坐标,而translationX、translationY是View左上角相对于父容器的偏移量。
x=left+translationX y=top+translationY 
View在平移过程中,top和left表示的是原始左上角的位置信息,其值并不会发生改变,此时发生改变的是X、 Y、translationX、translationY

MotionEvent和TouchSlop

1 MotionEvent

在手指接触屏幕后产生的一系列事件中,典型的事件类型如下几种
常见的动作常量:    public static final int ACTION_DOWN             = 0;单点触摸动作    public static final int ACTION_UP               = 1;单点触摸离开动作    public static final int ACTION_MOVE             = 2;触摸点移动动作    public static final int ACTION_CANCEL           = 3;触摸动作取消     public static final int ACTION_OUTSIDE          = 4;触摸动作超出边界    public static final int ACTION_POINTER_DOWN     = 5;多点触摸动作    public static final int ACTION_POINTER_UP       = 6;多点离开动作   以下是一些非touch事件    public static final int ACTION_HOVER_MOVE       = 7;    public static final int ACTION_SCROLL           = 8;    public static final int ACTION_HOVER_ENTER      = 9;    public static final int ACTION_HOVER_EXIT       = 10;

掩码常量ACTION_MASK = 0X000000ff动作掩码 ACTION_POINTER_INDEX_MASK = 0X0000ff00触摸点索引掩码ACTION_POINTER_INDEX_SHIFT = 8 获取触摸点索引需要移动的位数

相关方法

getAction()方法返回的是int类型,用到的只有低16位,其中:低八位是动作的类型,高8位是触摸点索引值的表示(单点为0,双点为1)

获得动作类型: int action = event.getAction() &ACTION_MASK 或者使用 getActionMasked()

获得触摸点索引类型: int pointerIndex = (event.getAction() &ACTION_POINTER_INDEX_MASK ) >>ACTION_POINTER_INDEX_SHIFT

或者使用 getActionIndex()

为什么要有索引信息?

有了索引信息,我们可以在onTOuchEvent事件中判断传进来的MotionEvent对象对应的是单点信息还是多点信息。

下面的代码段能使用户在屏幕上拖动一个对象。它记录了初始点的位置,计算点移动的距离,并将对象移动到新的位置。它正确的处理了这种情况:当第一个手指把控件拖到一个位置,然后按下第二个手指,且第二个手指与同一个控件上。当用户抬起第一个手指时,控件不会跑到第二个手指的位置同时第二个手指可以继续拖动控件。


// The ‘active pointer’ is the one currently moving our object.private int mActivePointerId = INVALID_POINTER_ID;@Overridepublic boolean onTouchEvent(MotionEvent ev) {    // Let the ScaleGestureDetector inspect all events.    mScaleDetector.onTouchEvent(ev);                 final int action = MotionEventCompat.getActionMasked(ev);             switch (action) {     case MotionEvent.ACTION_DOWN: {        final int pointerIndex = MotionEventCompat.getActionIndex(ev);         final float x = MotionEventCompat.getX(ev, pointerIndex);         final float y = MotionEventCompat.getY(ev, pointerIndex);                     // Remember where we started (for dragging)        mLastTouchX = x;        mLastTouchY = y;        // Save the ID of this pointer (for dragging)        mActivePointerId = MotionEventCompat.getPointerId(ev, 0);        break;    }                case MotionEvent.ACTION_MOVE: {        // Find the index of the active pointer and fetch its position        final int pointerIndex =                 MotionEventCompat.findPointerIndex(ev, mActivePointerId);                      final float x = MotionEventCompat.getX(ev, pointerIndex);        final float y = MotionEventCompat.getY(ev, pointerIndex);                    // Only move if the ScaleGestureDetector isn't processing a gesture.        if (!mScaleDetector.isInProgress()) {            // Calculate the distance moved            final float dx = x - mLastTouchX;            final float dy = y - mLastTouchY;            mPosX += dx;            mPosY += dy;            invalidate();        }        // Remember this touch position for the next move event        mLastTouchX = x;        mLastTouchY = y;        break;    }                case MotionEvent.ACTION_UP: {        mActivePointerId = INVALID_POINTER_ID;        break;    }                case MotionEvent.ACTION_CANCEL: {        mActivePointerId = INVALID_POINTER_ID;        break;    }            case MotionEvent.ACTION_POINTER_UP: {                    final int pointerIndex = MotionEventCompat.getActionIndex(ev);         final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);         if (pointerId == mActivePointerId) {            // This was our active pointer going up. Choose a new            // active pointer and adjust accordingly.            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;            mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex);             mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex);             mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);        }        break;    }    }           return true;}


2、TouchSlop

touchSlop是系统所能识别出认为滑动的最小具体, getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如viewpager就是用这个距离来判断用户是否翻页

VelocityTracker 、GestureDetector、 Scroller

velocityTracker

android.view.VelocityTracker主要用跟踪触摸屏事件(flinging事件和其他gestures手势事件)的速率。用addMovement(MotionEvent)函数将Motion event加入到VelocityTracker类实例中.你可以使用getXVelocity() 或getXVelocity()获得横向和竖向的速率到速率时,但是使用它们之前请先调用computeCurrentVelocity(int)来初始化速率的单位 。主要函数Public Methods void addMovement(MotionEvent event) Add a user's movement to the tracker. void clear() Reset the velocity tracker back to its initial state. void computeCurrentVelocity(int units, float maxVelocity) Compute the current velocity based on the points that have been collected.int unitis表示速率的基本时间单位。unitis值为1的表示是,一毫秒时间单位内运动了多少个像素, unitis值为1000表示一秒(1000毫秒)时间单位内运动了多少个像素floatVelocity表示速率的最大值 void computeCurrentVelocity(int units) Equivalent to invoking computeCurrentVelocity(int, float) with a maximum velocity of Float.MAX_VALUE. abstract T getNextPoolable() float getXVelocity() Retrieve the last computed X velocity. float getXVelocity(int id) Retrieve the last computed X velocity. float getYVelocity(int id) Retrieve the last computed Y velocity. float getYVelocity() Retrieve the last computed Y velocity. abstract boolean isPooled() static VelocityTracker obtain() Retrieve a new VelocityTracker object to watch the velocity of a motion. void recycle() Return a VelocityTracker object back to be re-used by others. abstract void setNextPoolable(T element) abstract void setPooled(boolean isPooled)  示例:     private VelocityTracker mVelocityTracker;//生命变量     //在onTouchEvent(MotionEvent ev)中     if (mVelocityTracker == null) {             mVelocityTracker = VelocityTracker.obtain();//获得VelocityTracker类实例     } www.2cto.com    mVelocityTracker.addMovement(ev);//将事件加入到VelocityTracker类实例中     //判断当ev事件是MotionEvent.ACTION_UP时:计算速率     final VelocityTracker velocityTracker = mVelocityTracker;     // 1000 provides pixels per second     velocityTracker.computeCurrentVelocity(1, (float)0.01); //设置maxVelocity值为0.1时,速率大于0.01时,显示的速率都是0.01,速率小于0.01时,显示正常     Log.i("test","velocityTraker"+velocityTracker.getXVelocity());                         velocityTracker.computeCurrentVelocity(1000); //设置units的值为1000,意思为一秒时间内运动了多少个像素     Log.i("test","velocityTraker"+velocityTracker.getXVelocity()); 大体的使用是这样的:当你需要跟踪触摸屏事件的速度的时候,使用obtain()方法来获得VelocityTracker类的一个实例对象在onTouchEvent回调函数中,使用addMovement(MotionEvent)函数将当前的移动事件传递给VelocityTracker对象使用computeCurrentVelocity  (int units)函数来计算当前的速度,使用 getXVelocity  ()、 getYVelocity  ()函数来获得当前的速度

速度=(终点位置-起点位置)/时间段落
最后要记得回收内存
velocityTracker.clear();
velocityTracker.recycle();

GestureDetector

手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为, GestureDetector属于android.view包,android还提供了android.gesture包支持更多的手势操作,以后我们会介绍到。官方的介绍中使用了GestureDetectorCompat处理手势识别,为什么使用GestureDetectorCompat替换了GestureDetector呢,官方的是这样解释的:

GestureDetector类对外提供了两个接口:OnGestureListener,OnDoubleTapListener,还有一个内部类SimpleOnGestureListener;SimpleOnGestureListener类是GestureDetector提供给我们的一个更方便的响应不同手势的类,它实现了上述两个接口,该类是static class,也就是说它实际上是一个外部类,我们可以在外部继承这个类,重写里面的手势处理方法。因此实现手势识别有两种方法,一种实现OnGestureListener接口,另一种是使用SimpleOnGestureListener类。
OnGestureListener有下面的几个动作:
按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。
抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。
长按(onLongPress): 手指按在持续一段时间,并且没有松开。
滚动(onScroll): 手指在触摸屏上滑动。
按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。
抬起(onSingleTapUp):手指离开触摸屏的那一刹那。


使用OnGestureListener接口,这样需要重载OnGestureListener接口所有的方法,适合监听所有的手势,正如官方文档提到的“Detecing All Supported Gestures”。

View类有个View.OnTouchListener

通过重写他的onTouch(View v, MotionEvent event)方法,我们可以处理一些touch事件,如下:
public class MainActivity extends Activity {...// This example shows an Activity, but you would use the same approach if// you were subclassing a View.@Overridepublic boolean onTouchEvent(MotionEvent event){             int action = MotionEventCompat.getActionMasked(event);            switch(action) {        case (MotionEvent.ACTION_DOWN) :            Log.d(DEBUG_TAG,"Action was DOWN");            return true;        case (MotionEvent.ACTION_MOVE) :            Log.d(DEBUG_TAG,"Action was MOVE");            return true;        case (MotionEvent.ACTION_UP) :            Log.d(DEBUG_TAG,"Action was UP");            return true;        case (MotionEvent.ACTION_CANCEL) :            Log.d(DEBUG_TAG,"Action was CANCEL");            return true;        case (MotionEvent.ACTION_OUTSIDE) :            Log.d(DEBUG_TAG,"Movement occurred outside bounds " +                    "of current screen element");            return true;              default :             return super.onTouchEvent(event);    }      }

Scroller

Android里Scroller类是为了实现View平滑滚动的一个Helper 类。通常在自定义的View时使用,在View中定义一个私有成员mScroller = new Scroller(context)。设置mScroller滚动的位置时,并不会导致View的滚动,通常是用mScroller记录/计算View滚动的位置,再重写View的computeScroll(),完成实际的滚动。
相关API介绍如下
mScroller.getCurrX() //获取mScroller当前水平滚动的位置   mScroller.getCurrY() //获取mScroller当前竖直滚动的位置   mScroller.getFinalX() //获取mScroller最终停止的水平位置   mScroller.getFinalY() //获取mScroller最终停止的竖直位置   mScroller.setFinalX(int newX) //设置mScroller最终停留的水平位置,没有动画效果,直接跳到目标位置   mScroller.setFinalY(int newY) //设置mScroller最终停留的竖直位置,没有动画效果,直接跳到目标位置      //滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量, duration为完成滚动的时间   mScroller.startScroll(int startX, int startY, int dx, int dy) //使用默认完成时间250ms   mScroller.startScroll(int startX, int startY, int dx, int dy, int duration)      mScroller.computeScrollOffset() //返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。   
举例说明,自定义一个CustomView,使用Scroller实现滚动:
import android.content.Context;  import android.util.AttributeSet;  import android.util.Log;  import android.view.View;  import android.widget.LinearLayout;  import android.widget.Scroller;    public class CustomView extends LinearLayout {        private static final String TAG = "Scroller";        private Scroller mScroller;        public CustomView(Context context, AttributeSet attrs) {          super(context, attrs);          mScroller = new Scroller(context);      }        //调用此方法滚动到目标位置      public void smoothScrollTo(int fx, int fy) {          int dx = fx - mScroller.getFinalX();          int dy = fy - mScroller.getFinalY();          smoothScrollBy(dx, dy);      }        //调用此方法设置滚动的相对偏移      public void smoothScrollBy(int dx, int dy) {            //设置mScroller的滚动偏移量          mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);          invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果      }            @Override      public void computeScroll() {                //先判断mScroller滚动是否完成          if (mScroller.computeScrollOffset()) {                        //这里调用View的scrollTo()完成实际的滚动              scrollTo(mScroller.getCurrX(), mScroller.getCurrY());                            //必须调用该方法,否则不一定能看到滚动效果              postInvalidate();          }          super.computeScroll();      }  }  










更多相关文章

  1. Eclipse项目导入Android(安卓)Studio,.9图片报错解决办法
  2. Android中LocationManager的简单使用01
  3. Arcgis For Android之GPS定位实现
  4. Android实现获取验证码的倒计时功能
  5. android --多线程下载
  6. UnsatisfiedLinkError: dalvik.system.PathClassLoader nativeLi
  7. android 在Resources 中定义arrays
  8. Android(安卓)贝塞尔曲线 折线图
  9. android 学习之popupWindow 在指定位置上的显示,进出样式。

随机推荐

  1. Android添加USB连接提示音(SettingsProvid
  2. 如何在Android设备中创建Web服务器(用于远
  3. android应用私有存储文件的写入与读取-op
  4. 如何从Android中的人脸检测部分找出脸部
  5. 为什么我们应该将XMPP而不是JSON用于简单
  6. ViewPager滚动很卡的问题
  7. android开发笔记之java的reflect的理解(在
  8. NullPointerException: android.support.
  9. . lang。NoClassDefFoundError:未解决的:
  10. 百度——个人云存储pcs——Android使用百