《Android进阶之光》读书笔记(二)

Posted by alonealice on 2017-07-17

View体系和自定义View

点击事件坐标系

getX():获取点击事件距离控件左边的距离,即视图坐标
getY():获取点击事件距离控件左边的距离,即视图坐标
getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标
getRawY():获取点击事件距离整个屏幕顶边的距离,即绝对坐标

View的滑动

Android View移动的6中方法:layout()offsetLeftAndRight()offsetTopAndBottom()
LayoutParams,动画,scrollToscrollByscroller
offsetLeftAndRight()offsetTopAndBottom()里面传递的是偏移量。

动画完成后,会回到原来的位置,我们可以在xml中加上fillAfter="true",使动画保持在最后一个状态。但是即使这样,view的实际位置依旧在原来的地方,view动画不能改变view的属性。

scrollToscrollBy移动的是View的内容,他们的移动可以看做是手机的移动。

Scroller本生不能实现View的滑动,需要跟View的computeScroll方法配合才能实现滑动效果。

Scroller在执行startScroll时 只是设置一些参数,同时调用View的onDraw方法,onDraw()方法会调用computeScroll方法。在computeScroll方法中,我们一般会调用computeScrollOffset()方法。这个方法会判断是否已经滑动完毕,如果未完毕,则根据时间和差值器获取当前滑动的位置。

View动画有一个大的缺陷,就是不具有交互性,所以View动画只能做普通的动画效果,要避免交互操作,但是优点是效率高,使用方便。
属性动画可用的动画类型:translationXtranslationY(延X或Y轴移动),rotationrotationXrotationY(围绕支点进行旋转),PrivotXPrivotY(控制支点位置),alpha(透明度),xy(View的最终位置)。

可以通过添加getset方法添加属性动画的类型,在set方法中使用requestLayout()重新显示View。
ValueAnimator不提供任何动画效果,它是一个数值发生器,用来产生一定的规律的数字,从而控制动画实现。

在使用组合动画AnimatorSet动画时,需要先用到AnimatorSetplay方法,该方法builder,builder使用建造者模式构建,当中包括4个方法:
after(Animator animator):将现有动画插入到传入的动画之后执行
after(long delay):将现有动画延迟指定时间执行
before(Animator anim):将现有动画插入到指定动画前执行
with(Animator anim):将现有动画和传入动画同时执行

组合动画除了使用AnimatorSet,还可以使用PropertyValuesHolder,当然,我们得结合OnjectAnimator.ofPropertyValuesHolder方法来使用,当然,这种方法动画只能同时进行。代码如下:

1
2
3
PropertyValuesHolder holder1=PropertyValuesHolder.ofFloat("scaleX",1.0f,1,5f);
...
ObjectAnimator animator=ObjectAnimator.ofPropertyValuesHolder(view,holder1,holder2,holder3);

除此之外,还可以使用xml来进行动画,并通过AnimatorInflater.loadAnimator方法来加载动画。

View事件分发

一个Activity对象包含一个Window对象,这个对象由PhoneWindow来实现。PhoneWindowDecorView作为整个应用窗口的根View,而这个DecorView又将屏幕分为两个区域,一个是TitleView,另一个是ContentView,而我们的布局都展示在ContentView中。

事件分发的重要3个方法:
dispatchTouchEvent(MotionEvent ev)————用来进行事件的分发
onInterceptTouchEvent(MotionEvent ev)————用来进行事件的拦截(dispatchTouchEvent 方法中执行,在View中不存在,在Viewgroup中)
onTouchEvent(MotionEvent ev)————用来处理点击事件

分发机制具体的流程:
点击事件产生后,事件先会传递给当前的Activity,这会调用ActivitydispatchTouchEvent方法,具体的工作会交由PhoneWindow来完成。然后PhoneWindow再把工作交由DecorView,之后再有DecorView把事件处理交由根ViewGroup

ViewGroupdispatchTouchEvent方法中,首先要先判断事件是否是DOWN事件,如果是,则进行初始化。因为任何一个动作的开始都是由DOWN开始,所以要先初始化。接下来要判断是否这个ViewGroup是否拦截了这个事件,即mFirstTouchTarget是否为null,拦截了就为null,如果既不是DOWN事件,有拦截了(mFirstTouchTarget==null)那么intercept就直接为true,表示拦截了当次事件。当拦截的情况下。则调用onInterceptTouchEvent方法判断是否依旧拦截。子View可以通过requestDisallowInterceptTouchEvent来设置是否禁止拦截器拦截除了DOWN以外的事件。
ViewGroup在传递子View的事件时,view的顺序是从上往下传递的,即最新添加的最先执行。同时,View的onTouchListener事件比onTouchEvent有更高的优先级,如果onTouchListener返回true,说明已经消费掉事件,那么onTouchEvent不再执行。

Activity显示的流程:
当我们在调用startActivity时,会调用ActivityThreadhanldeLaunchActivity来创建activity。创建时会调用到oncreate方法,从而完成DecorView的创建。然后会执行handleResumeActivity方法,在这个方法里面,会调用Activity的onResume方法。随后会得到DecorViewWindowManagerViewManager,并且调用其实现类WindowManagerImpladdView。在WindowManagerImpladdView方法中,调用WindowManagerGlobaladdView方法,而在这个方法中创建ViewRootImpl实例,并且调用setView()方法,将DecorView加载到Window中。
同时,ViewRootImpl还会调用PerformTraveals方法,使ViewTree开始View的工作流程。该方法会依次执行performMeasureperformLayout
performDraw方法。

MeasureSpec代表了32位的int值,其中高2位是SpecMode(测量模式),低30位是SpecSize(测量尺寸)
SpecMode的3种模式:
UNSPECIFIED:未指定模式,View想多大就多大,不会受父容器限制。
AT_MOST:最大模式,对应于wrap_content属性,子View的最终大小是父View指定的SpecSize,并且最大不能大于这个值。
EXACTLY :精确模式,对应于match_content属性,父容器测量出View所需要的大小,就是SpecSize的值。