有关View的知识点

一、View的位置参数

1.View的四个属性:top,left,right,bottom.注意,这些坐标都是相对于View的父容器来说的,是相对坐标

有关View的知识点_第1张图片

Left = getLeft();

Right = getRight();

Top = getTop();

Bottom = getBottom();

从Android3.0开始,View增加了额外的几个参数:x,y,translationX,translationY。 其中x,y是View的左上角的坐标,translationX和translationY是View左上角相对于父容器的偏移量,这几个参数也是相对于父容器的,translationX和translationY的,默认值是0

注意View在平移的过程中,top和left表示的是原始的左上角位置信息,其值并不会发生改变,此时发生改变的是x,y,translationX,translationY

2.MontionEvent和TouchSlop

通过MontionEvent对象可以得到点击事件发生的x和y坐标  getX/getY  返回的是相对于当前View左上角坐标     getRawX/getRawY返回相对于手机屏幕的左上角坐标

TouchSlop

 是系统所能识别的滑动的最小距离  ViewConfiguration.get(getContext()).getScaleTouchSlop()

3.VelocityTracker,GestureDetector和Scroll

VelocityTracker速度追踪,用于追踪手指滑动过程中的速度,包括水平和竖直的速度,在View的onTouchEvent方法中追踪当前单击事件的速度

VelocityTracker   velocityTracker = VelocityTracker.obain()

velocityTracker.addMovement(event)

velocityTracker.computeCurrentVelocity(1000);

int xVelocity = (int)velocityTacker.getXVelocity();

最后在不需要使用的时候,调用clear方法来重置回收内存velocityTracker.clear(),velocityTrackler.recycle();

4.GestureDetector

方法名 描述 所属接口
onDown 手指轻轻触摸屏幕的一瞬间,由一个ACTION_DOWN触发 OnGestureListener
onShowPress 手指轻轻触摸屏幕,尚未松开或拖动,由一个ACTION_DOWN触发 *注意和onDown()的区别,他强调没有松开或者拖动的状态 OnGestureListener
onSingleTapUp 手指(轻轻触摸屏幕后)松开,伴随着1个MontionEvent ACTON_UP而触发,这是单击行为 OnGestureListener
onScroll 手指按下屏幕并拖动,由1个ACTION_DOWN,多个ACTION_MOVE触发,这是拖动行为 OnGestureListener
onLongPress 用户长时间按着不放,即长按 OnGestureListener
onFling 用户按下触摸屏,快速滑动,由1个ACTION_DOWN.多个ACTION_MOVE,和一个ACTION_UP触发,这是快速滑动行为 OnGestureListener
onDoubleTap 双击,由两次连续的单击组成,他不可能和onSingleTapConfirm共存 OnDoubleTapListener
onSingleTapConfirm 严格的单击行为  他与onSingleTap的区别,如果触发了onSingleTapConfirm,那么后面不可能在紧跟另一个单击行为,即只可能是单击,而不可能是双击中的一次单击  
onDoubleTapEvent 表示发生了双击行为,在双击期间,ACTION_DOWN,_ACTION_UP都会触发此回调  

建议:如果只是监听滑动相关的,在onTouchEvent中实现,如果要监听双击这种行为的话,使用GestureDetector

二、View的滑动

1.使用scrollTo/scrollBy

View内部的两个属性mScrollX,mScrollY,这两个属性通过getScrollX和getScrollY获得,在滑动过程中,mScrollX的值为View左边缘和View内容边缘的水平方向的距离,而mScrollY的值等于View上边缘和View内容上边缘在竖直方向的距离,scrollBt和scrollTo只能翻改变View内容上的位置而不能改变view在布局中的位置

2.使用动画

ObjectAnimator.ofFloat(targetView,"translationX",0,100).setDuration(100).start();注意,View动画是对View的影像做操作,并不是真正的改变View的位置参数,如果希望动画后的状态能够保留还必须将fillAfter属性市设为true

3.改变布局参数

MarginLayoutParams params = (MarginLayoutParams)mButton.getLayoutParams();

params.width +=100;

params.leftMargin +=100;

mButton.requestLayout();//mButton.setLayoutParams();

4.弹性滑动

4.1使用Scroller

Scroller scroller = new Scroller(context);

private void smoothScrollTo(int destX,int destY){

  int scrollX = getScrollX();

  int deltaX = dextX-scrollX;

  mScroller.startScroll(scroll,0,destX,0,1000);

  invalidate();

}

@override

public void computerScroll(){
  if(mScroller.computerScrollOffset()){

    scrollTo(mScroller.getCurrX(),mScroll.getCurrY());

    postInvalidate();

  }

}

4.2通过动画

ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(1000);

animation.addUpdateListener(new AniatorUpdateListener(){

  @override

  public void onAniamtionUpdate(ValueAnimator animator){

    float fraction = animator.getAnimatedFraction();

    mButton.scrollTo(startX+(int)(deltaX*fraction),0);

  }

})

animation.start();

4.3使用延迟策略

核心思想:通过发送一系列延迟消息从而达到一种渐进式的效果,具体说可以使用Handle或View的postDelayed方法

private Handler mHandler = new Handle(){

  public void handleMessaege(Message msg){

    switch(msg.what){

      case MESSAGE_SCROLL_TO:

         mCount++;

        if(mCount<=FRAME_COUNT){

            float fraction = mCount/(int)FRAME_COUNT;

            int scrollX = (int)(fraction*100);

            mButton.scrollTo(scrollX,0);

            mHandler.sentEmptyMessageDelayed(MESSAGE_SCROLL_TO,DDEAYED_TIME);

        }

        BREAK;

    }

  }

}

三、View的事件分发机制

1.点击事件传递规则

public boolean dispatchTouchEvent(MontionEvent e)

用来进行事件分发,如果时间能够传递给当期View,那么此方法一定会调用,返回的结果手当前View的onTouchEvent和下级的View的dispatchTouchEvent方法影响,表示是否消耗当期事件

public boolean onInterceptTouchEvent(MontionEvent e)

用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会再次被调用,返回结果表示是否拦截当前事件

public boolean onTouchEvent(Montion e)

在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件

伪代码:

public void dispatchTouchEvent(MontionEvent ev){

  boolean consume = false;

  if(onInterceptTouchEvent(ev)){

      consume = onTouchEvent(ev);

  }else{

    consume = child.dispatchTouchEvent(ev);

  }

 return consume;

}

事件传递Activity -> window -> View 如果所有元素都不处理这个事件,那么这个事件最终传递给Activity处理,即Activity的onTouchEvent方法会被调用

关于时间传递的结论

(1)同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件,这个事件以DOWN时间开始,中间含有数量不定的move事件,最终以up结束

(2)正常情况下,一个事件序列只能被一个View拦截且消耗,因为一旦一个元素拦截了此事件,那么同一个事件序列内的所有事件都会直接交给他处理,因此同一个事件序列中的事件不能分别有两个View同时处理,但是通过特殊手段可以做到,比如一个View将本该自己处理的事件通过onTouchEvent强行传递个给其他View处理

(3)某个View一旦决定拦截,那么这个事件序列都只能有他来处理,并且它的onInterceptTouchEvent不会再被调用

(4)某个View一旦开始处理事件,如果不消耗ACTION_DOWN事件(onTouchEvent返回false),那么同一事件序列中的其他事件都不会再交给他来处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent会被调用。意思是事件一旦交给一个View处理,那他必须消耗掉,否则同一事件中剩下的事件就不再交给他来处理了。

(5)如果View不消耗ACTION_DOWN以外的其他事件,那么这个点击事件会消失。此时父元素的onTouchEvent事件并不会调用,并且当前的View可以持续收到后续的事件,最终这些消失的事件会传递给Avtivity处理。

(6)ViewGroup默认不拦截任何事件,Android源码中ViewGroup的onInterceptTouchEvent方法默认返回false

(7)View没有onInterceptTouchEvent方法,一旦事件传递给他,那么它的onTouchEvent方法就会被调用

(8)View的onTouchEvent默认都会消耗事件(返回true),除非他是不可点击的(clickable和longClickable同时为false)

(9)View的enable属性不影响onTouchEvent的默认返回值,只要clickable或者longClickable有一个为true,那么他的onTouchEvent就返回true

(10)onClick会发生的前提是当前View是点击的,并且他收到了down和up事件

(11)事件传递过程是由外向内的,即事件总是先传递给父元素,然后再由父元素分发给子View,通过requeseDisallowInterceptTouchEvent方法可以子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外


总结onInterceptTouchEvent不是每次事件都会调用的,如果想提前处理所有点击事件,要选择dispatchT ouchEvent方法,只有这个方法确保每次都会调用




你可能感兴趣的:(有关View的知识点)