Android知识点汇总--第二篇

View基础

View的位置参数:top,left,right,bottom。都是相对于View的父容器来说的。
Android3.0以后增加了:x,y,translationX,translationY。x,y为View左上角坐标,translationX,translationY是View左上角相对于父容器的偏移量。
在点击事件中:
getX/getY返回当前View左上角的x和y坐标。getRawX/getRawY返回手机屏幕左上角x/y坐标。
最小滑动距离:

ViewConfiguration.get(getContext()).getScaledTouchSlop()

VelocityTracker/GestureDetector/Scroller

VelocityTracker:
在View的onTouchEvent方法中追踪当前单击事件的速度:

VelocityTracker vt = VelocityTracker.obtain();
vt.addMovement(event);
......
vt.computeCurrentVelocity(1000);//计算1000ms之内移动的像素数
int xVt = (int)vt.getXVelocity();//x方向的速度
int yVt = (int)vt.getYVelocity();
......
//在不需要使用的时候,调用clear方法重置并回收内存
vt.clear();
vt.recycler();

GestureDetector:
可以用来检测用户的单击、滑动、长按、双击行为。可以实现OnGestureListener或者onDoubleTapListener来监听双击行为。如果只监听滑动事件,直接在onTouchEvent方法中处理就可以了,如果要监听双击行为,就需要使用GestureDetector。
创建GestureDetector对象以后,在View的onTouchEvent方法中添加:

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

Scroller:
Scroller本身无法让View弹性滑动,需要和View的computeScroll方法配合使用才能共同完成。通过scrollTo和scrollBy来完成,它不断让View重绘,而每一次重绘距滑动起始时间会有一个时间间隔,通过这个时间间隔Scroller就可以得出View当前的滑动距离,再通过scrollTo来完成滑动。
scrollBy内部调用scrollTo方法,scrollTo方法实现了基于所传递参数的绝对滑动。这两个方法只改变View内容的位置而不改变View在布局中的位置。
从左—–>右滑动:mScrollX为负值。
从上—–>下滑动:mScrollY为负值。

弹性滑动

Scroller/动画/延时策略
采用动画来实现滑动动画:

ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(1000);
animator.addUpdateListener(new AnimatorUpdateListener(){
    @Override
    public void onAnimationUpdate(ValueAnimator animator){
        float fraction = animator.getAnimatedFraction();
        mView.scrollTo(startX+(int)(deltaX*fraction),0);
    }
});

延时策略:
通过发送一系列延时消息从而达到滑动效果。可以使用Handler,postDelayed,sleep来完成。

View事件分发机制

涉及到三种主要方法:dispatchTouchEvent/onInterceptTouchEvent/onTouchEvent。

public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume = false;
    if(onInterceptTouchEvent(ev)){ //根ViewGroup是否拦截
        consume = onTouchEvent(ev);
    }else{
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}

优先级:OnTouchListener>onTouchEvent>OnClickListener
点击事件传递过程:Activity->Window->View。
如果子View的onTouchEvent返回false,则会调用父容器的onTouchEvent方法。

  1. ViewGroup默认不拦截任何事件。
  2. View没有onInterceptTouchEvent方法,一旦有点击事件给它,那么它的onTouchEvent方法会被调用。
  3. View的onTouchEvent默认都会消耗事件,除非它的clickable属性为false。
  4. View的enable属性不影响onTouchEvent的默认返回值。
  5. 事件传递过程总是先传递给父元素,然后通过父元素分发给子View。在子元素中可以通过requestDisallowInterceptTouchEvent方法敢于父元素的分发过程,ACTION_DOWN事件除外。对于ACTION_DOWN事件,ViewGroup总是会调用自己的onInterceptTouchEvent方法来询问是否要拦截事件。
  6. ViewGroup决定拦截事件后,后续的点击操作会默认交给它处理并且不再调用它的onInterceptTouchEvent方法。如果我们想提前处理所有的点击事件,要选择dispatchTouchEvent方法,只有这个方法会每次都会调用,前提是点击事件能够传递到当前的ViewGroup。

事件传递顺序:上级->下级。
事件处理顺序:下级->上级。

事件传递时候先调用dispatchTouchEvent方法,再执行onInterceptTouchEvent方法。返回值true,表示拦截不继续。
事件处理都是调用onTouchEvent方法,返回true,表示处理了,不用审核了。
初始情况下,返回值都是false。

滑动冲突解决方案

外部拦截法和内部拦截法
外部拦截法:
重写父容器的onInterceptTouchEvent方法。在ACTION_DOWN和ACTION_UP这两种情况下需要返回false,在ACTION_MOVE方法中根据条件判断父容器是否需要拦截。
内部拦截法:
父容器不拦截任何事件,所有事件传递给子元素。需要配合requestDisallowInterceptTouchEvent方法才能正常工作。在ACTION_DOWN的时候,子元素需要调用parent.requestDisallowInterceptTouchEvent(true)。
父元素也要默认拦截除了ACTION_DOWN以外的其他事件,这样当子元素调用parent.requestDisallowInterceptTouchEvent(false)方法时,父元素才能继续拦截所需事件。一旦父容器拦截了ACTION_DOWN事件,所有的事件都无法传递到子元素中。

ViewRoot和DecorView

ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的measure、layout、draw都是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完成后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,将ViewRootImpl和DecorView之间建立关联。

View的绘制

View工作流程指measure/layout/draw三个流程。
View的最终大小是在layout阶段确定的。
直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content相当于使用match_parent。
解决View测量的四种方法:
1. onWindowFocusChanged方法
View已经初始化完毕了,当Activity窗口得到和失去焦点的时候都会调用一次。
2. view.post(runnable)
通过post也可以将一个runnalbe投递到消息队列的尾部,然后等待Looper调用此runnable的时候,View已经初始化好了。

protected void onStart(){
    super.onStart();
    view.post(new Runnable(){
        @Ovriride
        public void run(){
            int width = view.getMeasureWidth();
            int height = view.getMeasureHeight();
        }
    });
}
  1. ViewTreeObserver
    使用它的众多回调可以完成这个功能。可以通过OnGlobalLayoutListener这个接口。当View树的状态发生改变或者View树内部的View的可见性发生改变时,onGlobalLayout方法将会被回调。
protected void onStart(){
    super.onStart();
    ViewTreeObserver observer = view.getViewTreeObserver();
    observer.addOnGloalLayoutListener(new OnGlobalLayoutListener(){
        @Override
        public void onGlobalLayout(){
            view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            int width = view.getMeasureWidth();
            int height = view.getMeasureHeight();
        }
    });
}
  1. view.measure(int widthMeasureSpec,int heightMeasureSpec)
    通过手动测量。

Layout的作用是ViewGroup用来确定子元素位置的,在layout方法中确定View本身的位置,在onLayout方法中确定所有子元素的位置。
View的绘制过程遵循以下几步:

  1. 绘制背景 background.draw(canvas)
  2. 绘制自己 onDraw
  3. 绘制children dispatchDraw,dispatchDraw会遍历所有子元素的draw方法。
  4. 绘制装饰 onDrawScrollBars

自定义View的方法

需要注意的一些问题:
1.让View支持wrap_content。
2.直接继承View的控件,如果不在draw方法中处理padding,那么padding属性不起作用;如果直接继承ViewGroup,需要在onMeasure和onLayout中考虑padding和子元素的margin对其造成的影响。
3.在View的内部提供了post系列的方法,可以替代Handler。
4.View中的动画和线程,在onDetachedFromWindow中停止比较好。
5.View中存在滑动嵌套,需要考虑滑动冲突问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值