Android View的详解

Android View的作用不亚于其四大主件。这里说下其使用过程中的一些小细节:

View的属性参数:

    View的位置由四个顶点来决定,分别对应View的四个属性:top(左上角纵坐标),left(左上角横坐标),right(右下角横坐标),bottom(右下角纵坐标),即
通过getTop,getLeft,getRight,getBottom得到四个坐标
则view的宽为:width = getRight -getLeft
             高为   hidth = getBottom - getTop

同时View还有x,y ,translateX, translateY,
	    x和y表示View左上角的坐标
	    translateX和translateY是view左上角相对于父容器的偏移量,其默认值是0(表示未移动)
即换算关系为:x = left+translationX;
                       y = top+translationY;
View在平移过程中,top和left表示的是原始左上角的信息,,其值并不会发生变化,变化的是x,y,translatex,translatey。
 
View在滑动过程中产生的getX和getY,以及getRawX和getRawY
其中getX和getY表示返回的是相对于当前View左上角的x和y坐标
getRawX和getRawY表示返回的是相对于手机屏幕左上角的x和y的坐标

TouchSlop:指的是系统所能识别出的被认为是滑动的最小距离,当2次滑动的距离小于这个值时,系统则认为其不是滑动。其常量值获取方式为:ViewConfiguration.get(getContext()).getScaledTouchSlop().可用于优化滑动体验。

一般如果只是监听滑动相关的,建议在OnTouchEvent中实现,如果要监听双击这种行为的,就需要使用GestureDetector手势识别器。

View的滑动实现的三种方式:

   1,通过View本身提供的SrcollBy和SrcollTo来实现,SrcollBy实际上也调用了SrcollTo方法,在滑动过程中,mSrcollX的值总是等于其左View左边缘和View内容左边缘在水平方向的距离,mSrcollY的值总是等于其View上边缘和View内容上边缘在竖直方向的距离,View的边缘位置表示View的位置,四个顶点构成。同时其2者的滑动只能改变View的内容的位置而不能改变其View在布局中的位置。适用于对view内容的滑动。
   
   2,通过动画给View施加平移的效果来实现滑动。适用于没有交互的view和实现复杂的动画效果
   
   3,通过改变View的LayoutParams使得View重新布局而实现滑动。使用于有交互的view。

View事件分发机制

dispatchTouchEvent:用于事件的分发,如果事件能够传递给当前View,那么此方法一定会被调用,返回的结果受当前View的OnTouchEvent和下级View的onIntercepTouchEvent方法的影响,表示是否消耗当前事件。

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

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

View事件分发机制论点:

0,当一个View需要处理事件时,如果设置了OnTouchListener,那么其onTouch方法会被回调,如果其返回false,则当前View的onTouchEvent方法会被调用,反之,不会。说明了给View设置的onTouchListener事件比onTouchEvent事件优先级别高。

1,正常情况下,一个事件序列只能被一个View拦截且消耗。
2,某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递的化),并且它的onIntercepTouchEvent不会被再调用。

3,某个View一旦开始处理事件,如果它不消耗ACTION_DOWN(onTouchEvent返回了false),那么同一个事件序列中的其他事件都不会再交给它来处理,并且事件将重新交给它的父元素处理,即父元素的onTouchEvent会被调用。

4,ViewGroup默认不拦截任何事件,其ViewGourp的onInterceptTouchEvent方法默认返回了false。
5,View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么其onTouchEvent方法就会被调用。

6,View的onTouchEvent默认都会消耗事件(返回true),除非它时不可点击的(clickable和longclickable同时为false)。View的longclickable属性默认都为false。一般来说可点击的View的clickable默认是false,可点击的View的clickable属性为true。TestView的clickable默认属性为false,button的clickable属性默认时ture。

7,View的enable属性不影响onTouchEvent的默认返回值,默认返回ture。
8,onclick会发生的前提时当前View是可点击的,并且它收到了down和up的事件

9,事件传递过程是由外向内的,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。一旦设置了此方法,则ViewGourp将无法拦截除了ACTION_DOWN以外的其他事件,,因为ACTION_DOWN会重置其标记位,导致其方法失效。

10,传递过程是Activity--->window(phoneWindow)---->view(decorView顶层的view,其通过setContentView获得,一般顶层的View都是ViewGroup,看布局xml可得知)

View滑动冲突的场景:

1,外部滑动方向和内部滑动方向不一致,处理规则:当用户左右滑动时,需要让外部的View拦截点击事件,当用户上下滑动时,需要让内部的View拦截点击事件。
		
		**解决方案:**
        外部拦截法:指点击事件都先经过父容器的拦截处理,其需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可。其ACTION_DOWN(一旦返回了true,后续的移动和抬起事件都会交给父容器处理)和ACTION_UP都必须返回false。
        内部拦截法:指的时父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器进行处理,需要配合requestDIsallowInterceptTouchEvent方法才能进行处理,需要重写子元素的dispatchTouchEvent方法
 parent.requestDIsallowInterceptTouchEvent(true)表示通知父容器不拦截此事件。
 
2,外部滑动方向和内部滑动方向一致
3,以上2种情况的综合

针对外部滑动方向和内部滑动不一致解决方法之一(外部拦截法):

@Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            //父容器 默认不拦截按下事件
            intercepted = false;
            if (!mScroller.isFinished()) {
                Log.d(TAG,"SSS");
                mScroller.abortAnimation();
                intercepted = true;
            }
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastXIntercept;
            int deltaY = y - mLastYIntercept;
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                intercepted = true;
            } else {
                intercepted = false;
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            intercepted = false;
            break;
        }
        default:
            break;
        }

        Log.d(TAG, "intercepted=" + intercepted);
        mLastX = x;
        mLastY = y;
        mLastXIntercept = x;
        mLastYIntercept = y;

        return intercepted;
    }

View的工作细节:

    View的显示需要经过onDraw,onmeasure,onlayout三个过程,其measure决定了View的宽和高,可以通过getMeasureWidth和getMeasureHeight方法获取到View测量后的宽和高
    MeasureSpec(策略规格):MeasureSpec代表一个32位int值,高2位代表SpecMode,低30位代表SpecSize。SpecMode指的是测量模式,SpecSize指的是某种策略模式下的测量大小。
     
     SpecMode三种模式:
         1,UNSPECIFIED: 父容器不对View有任何限制,要多大给多大,一般用于系统内部
         2,EXACTLY:  父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这二种模式
         3,AT_MOST: 父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体的是什么值要看不同View的具体实现,它对应于LayoutParams中的wrap_content .
具体的View的测量模式和大小在这里插入图片描述

自定义View须知:

1,让View支持wrap_content,因为直接在布局中使用wrap_content是不生效的,所以需要在onMeasure中对wrap_content进行处理
2,如果有必要,让View支持padding属性。如果不在onDraw方法中处理padding,那么padding是无法起到作用的
3,尽量不要在View中使用Handle
4,View中如果有线程或者动画,需要及时停止,

以上就是View的一些认知。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值