View基础知识

来源:互联网 发布:小程序 post php接收 编辑:程序博客网 时间:2024/06/08 19:04

view的事件体系

在Android 中View不是四四大组件之一,但是它有这非常重要的地位。Android体统中为开发者提供了多种多样的空间如:TextView,Buttom,ListView等等。但是在实际开发中可能还是不能满足所有的需求,所有必不可少的要自定义控件。在自定义控件之前要对View有一个清晰的理解所以在这里记录下View的笔记,方便以后查阅。

View的基础知识

什么是view

view是所有Andriod控件的基类。View是界面的一个抽象类,它代表一个控件。ViewGruop是View的子类,而它是LinearLayout及RelativeLayout等的基类。ViewGruop如命表示它一个控件组,它可以包含多个控件。

View的坐标

View的坐标是由View的位置主要是他的四个属性来决定的如下图:

view坐标与父容器的关系

这样就很容易得出快高个View坐标的关系:width=right-left height=bottom-top

如何获取这几个属性呢?调用相应的get方法,如getRight();getLeft();等等。

MotionEvent和Touchslop

  • MotionEvent: 手指接触屏幕后的一系列事件,常有的事件有:
    ACTION_DOWN//手指刚刚接触显示屏
    ACTION_UP//手指抬起
    ACTION_MOVE//手指在屏幕上移动
    同时我们可以通过MotionEvent来获取点击事件发生的坐标x,y;通过getX,getY或getRawX,getRawY来获取;
    getX和getY得到的是相对于控件左上角的坐标;
    getRawX和getRawY得到的是相对于屏幕的的坐标;
  • TouchSlop: 这是一个常量,是系统能识别最小滑动距离

VelocityTracker、GestureDetector和Scroller

  • VelocityTracker : 翻译为:速度追踪器
    速度追踪器,用于追踪手指在滑动过程的速度,包括水平和竖直方向的速度。它的使用很简单,在View的onTouchEvnet方法中追踪当前单击的速度:

    VelocityTracker velocityTracker = VelocityTracker.obtain();velocityTracker.addMovement(event);

接着我们要知道滑动速度时,这个时候用如下的方法来获得当前的速度:

velocityTracker.computeCurrentVelocity(1000);int xVelocity =(int) velocityTracker.getXVelocity();int yVelocity =(int) velocityTracker.getYVelocity();

在这一步中有两点需要注意,第一点,获得速度之前必须要先计算速度,所以要先调用computeCurrentVeolocity方法;第二点,这里的速度是指一段时间内手指滑过的像素数;
速度 = (终点位置-起点位置)/时间段
最后当不需要它时需要调用clear方法重置并回收内存:

velocityTracker.clear();velocityTracker.recycle();
  • GestureDetector

手势探测:手势探测用于检测用户的单击、滑动、长按等行为。使用GestureDetector的方法如下:

GestureDetector mGestureDetector = new GestureDetector(this);//解决长按屏幕后无法拖动的现象mGestureDetector.setIsLongPressEnabled(flase);

接着在目标的View的onTouchEvent方法中如下实现:

boolean consume = mGestureDetector.onTouchEvent(event);return consume;

做完以上两步就可以选择性的实现OnGestrueListener和OnDoubleTapListener中的方法;

方法名 描述 所属接口 onDown 手指轻触屏幕的一瞬间,有一个ACTION_DOWN触发 OnGestrueListener onShowPress 手指亲触屏幕,尚未松开或拖动,由一个ACTION_DOWN触发
强调的是没有松开和拖动的状态 OnGestureListener onSingleTapUp 手指(轻触屏幕后)松开,伴随一个ACTION_UP而触发,这是个单击行为 OnGestureListener onScroll 手指按下屏幕并拖动,由一个ACTION_DOWN和多个ACTION_MOVE触发 OnGestureListener onLongPress 用户长按屏幕不放 OnGestureListure onFling 用户按下后、快速滑动然后松开,由1个ACTION_DOWN、多个ACTION_MOVE和1个ACTION_UP触发 OnGestuerListenrt onDoubleTap 双击,由2次连续的点击组成,它不可能和onSingleTapConfirmed共存 OnDoubleTapListener onSingleTapConfirmed 严格的单击行为 OnDoubleTapListener onDoubleTapEvnet 表示发生了双击行为,在双击的期间,ACTION_DOWN,ACTION_MOVE和ACTION_UP都会触发次回调 OnDoubleTapListener

Scroller

弹性滑动对象,用于View的弹性滑动。当使用View的scrollTo/scrollBy方法来进行滑动时,其过程是瞬间就完成的没有过渡效果。在这个情况下可以使用Scroller来实现这个过渡效果。Scroller本身无法让View弹性滑动,它需要和View的computeScroll方法配合才能共同完成这个功能。使用Scroller的基本方法如下:

Scroller scroller = new Scroller(mContext);/**缓慢滚动到指定位置*param destX 目标坐标的X坐标*param destY 目标坐标的Y坐标*dest 是 destination 简写*/private void smoothScrollTo(int destX,int destY){    int scrollX = getScrollX();    int delta = destX - scrollX;    //1000ms内滑向destX,效果是慢慢滑动    mScroller.startScroll(scrollX,0,delta,0,1000);    invalidate();}@overridepublic void computeScroll(){    if(mScroller.computeScrollOffset()){        scrollTo(mSroller.getCurrX(),mScroller.getCurrY());        postInvalidate();    }}

View的滑动

由于手机的屏幕有限,所以View需要将一些内容同通滑动来显示或隐藏;实现这个功能有三中方式:

  1. 通过View本身的scrollTo或scrollBy方法实现滑动;
  2. 通过动画来实现;
  3. 通过改变View的LayoutParams使得View从新布局来实现滑动;

使用scrollBy、scrollTo

View中提供了两个方法来实现滑动scrollTo、scrollBy来实现滑动的功能,源码如下:

/** * Set the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the x position to scroll to * @param y the y position to scroll to */public void scrollTo(int x, int y) {    if (mScrollX != x || mScrollY != y) {        int oldX = mScrollX;        int oldY = mScrollY;        mScrollX = x;        mScrollY = y;        invalidateParentCaches();        onScrollChanged(mScrollX, mScrollY, oldX, oldY);        if (!awakenScrollBars()) {            postInvalidateOnAnimation();        }    }}/** * Move the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the amount of pixels to scroll by horizontally * @param y the amount of pixels to scroll by vertically */public void scrollBy(int x, int y) {    scrollTo(mScrollX + x, mScrollY + y);}

scrollTo和ScrollBy只能改变View内容的位置而不能改变View在布局中的位置;在滑动的过程中我们要明白两个参数:mScrollX和mScrollY的改变,这两个参数可以通过getScrollX和getScrollY方法分别得到。下图可以形象的说明:

!(mScrollY和mScrollX的变换规律示意)[]

使用动画

通过动画我们能够让一个View进行平移,而平移就是一种滑动。使用动画来移动View,主要是操作View的TranslationX和TranslationY属性,即可以采用传统的View动画,也可以用属性动画,如果采用属性动画的话,为了能够兼容3.0以下的版本,需要使用开源动画库nineoldandriods(http:nineoldandroids.com)View动画的是对View的影像做操作,它并不能真正的改变View的位置参数,如果希望动画的状态得以保留必须将fillAfter属性设置为true,否则动画结束后动画结果会消失。

使用View动画并不能真正改变View的位置,当一个Button通过View动画向右移动100px,并且这个View设置有点击事件,之后你会发现点击事件无法触发onClick事件,而原来的位置任然可以触发onClick事件。因为不管Button怎么做变换,但是它的位置信息并不会随着动画而改变,因此在系统眼里,这个Button并没有发生任何改变,它的真身仍然在原始位置。

改变布局参数

通过改变LayoutParams的方式去实现View的滑动。

MarginLayoutParams params = (MarginLayoutParams)mButton.getLayoutParams();params.width += 100;params.leftMargin += 100;mButton.requestLayout();//或是mButton.setLayoutParams(params);

三种滑动的比较

以上三种滑动的 差异为:

  • scrollTo、scrollBy:操作简单,适合对View的内容滑动;
  • 动画:操作简单适合没有交互和实现复杂的动画效果;
  • 给变布局:操作复杂,适用于有交互的View;

弹性滑动

使用Scroller

Scroller的使用方法在上文提到了,下面是源码分析:

Scroller scroller = new Scroller(mContext);/**缓慢滚动到指定位置*param destX 目标坐标的X坐标*param destY 目标坐标的Y坐标*dest 是 destination 简写*/private void smoothScrollTo(int destX,int destY){    int scrollX = getScrollX();    int delta = destX - scrollX;    //1000ms内滑向destX,效果是慢慢滑动    mScroller.startScroll(scrollX,0,delta,0,1000);    invalidate();}@overridepublic void computeScroll(){    if(mScroller.computeScrollOffset()){        scrollTo(mSroller.getCurrX(),mScroller.getCurrY());        postInvalidate();    }}

//135-0820-2647
这里的工作原理是:我们构造一个Scroller对象调用它的startScroll方法时,Scroller内部什么也没有做,它只是保存了传递进去的几个参数。如下:

/** * Start scrolling by providing a starting point, the distance to travel, * and the duration of the scroll. *  * @param startX Starting horizontal scroll offset in pixels. Positive *        numbers will scroll the content to the left. * @param startY Starting vertical scroll offset in pixels. Positive numbers *        will scroll the content up. * @param dx Horizontal distance to travel. Positive numbers will scroll the *        content to the left. * @param dy Vertical distance to travel. Positive numbers will scroll the *        content up. * @param duration Duration of the scroll in milliseconds. */public void startScroll(int startX, int startY, int dx, int dy, int duration) {    mMode = SCROLL_MODE;    mFinished = false;    mDuration = duration;    mStartTime = AnimationUtils.currentAnimationTimeMillis();    mStartX = startX;    mStartY = startY;    mFinalX = startX + dx;    mFinalY = startY + dy;    mDeltaX = dx;    mDeltaY = dy;    mDurationReciprocal = 1.0f / (float) mDuration;}

这里就说的很清楚了,startScroll方法只是保存了一些状态,所以仅仅调用这个方法是没有办法让View滑动起来的;所以在startScroll的下面调用invalidate()这个方法。这个方法会导致View调用draw方法重绘View在draw方法中,用调用computeScroll,computeScroll方法在View中是一个空实现,这里要自己实现;具体机制是,View重绘后再draw方法中调用computeScroll,computeScroll向Scroller获取当前的scrollX和scrollY,然后通过ScrollTo方法实现滑动,接着调用postInvalidate方法进行重绘并重复之前的动作;

下面是computeScrollOffset()方法的实现。

 /** * Call this when you want to know the new location.  If it returns true, * the animation is not yet finished. */ public boolean computeScrollOffset() {    if (mFinished) {        return false;    }    int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);    if (timePassed < mDuration) {        switch (mMode) {        case SCROLL_MODE:            final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);            mCurrX = mStartX + Math.round(x * mDeltaX);            mCurrY = mStartY + Math.round(x * mDeltaY);            break;        case FLING_MODE:            final float t = (float) timePassed / mDuration;            final int index = (int) (NB_SAMPLES * t);            float distanceCoef = 1.f;            float velocityCoef = 0.f;            if (index < NB_SAMPLES) {                final float t_inf = (float) index / NB_SAMPLES;                final float t_sup = (float) (index + 1) / NB_SAMPLES;                final float d_inf = SPLINE_POSITION[index];                final float d_sup = SPLINE_POSITION[index + 1];                velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);                distanceCoef = d_inf + (t - t_inf) * velocityCoef;            }            mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;            mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));            // Pin to mMinX <= mCurrX <= mMaxX            mCurrX = Math.min(mCurrX, mMaxX);            mCurrX = Math.max(mCurrX, mMinX);            mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));            // Pin to mMinY <= mCurrY <= mMaxY            mCurrY = Math.min(mCurrY, mMaxY);            mCurrY = Math.max(mCurrY, mMinY);            if (mCurrX == mFinalX && mCurrY == mFinalY) {                mFinished = true;            }            break;        }    }    else {        mCurrX = mFinalX;        mCurrY = mFinalY;        mFinished = true;    }    return true;}

实现具体不说,就返回值非常重要Call this when you want to know the new location. If it returns true,the animation is not yet finished.这句简单的说就是但返回true的时候当前的动画还没有结束;

先写到这里View的事件分发、View的滑动冲突明后天在补上。

0 0
原创粉丝点击