View体系和自定义View
点击事件坐标系
getX()
:获取点击事件距离控件左边的距离,即视图坐标
getY()
:获取点击事件距离控件左边的距离,即视图坐标
getRawX()
:获取点击事件距离整个屏幕左边的距离,即绝对坐标
getRawY()
:获取点击事件距离整个屏幕顶边的距离,即绝对坐标
View的滑动
Android View移动的6中方法:layout()
,offsetLeftAndRight()
和offsetTopAndBottom()
,
LayoutParams
,动画,scrollTo
,scrollBy
,scroller
。
offsetLeftAndRight()
和offsetTopAndBottom()
里面传递的是偏移量。
动画完成后,会回到原来的位置,我们可以在xml中加上fillAfter="true"
,使动画保持在最后一个状态。但是即使这样,view的实际位置依旧在原来的地方,view动画不能改变view的属性。
scrollTo
和scrollBy
移动的是View的内容,他们的移动可以看做是手机的移动。
Scroller
本生不能实现View的滑动,需要跟View的computeScroll方法配合才能实现滑动效果。
Scroller
在执行startScroll
时 只是设置一些参数,同时调用View的onDraw
方法,onDraw()
方法会调用computeScroll
方法。在computeScroll
方法中,我们一般会调用computeScrollOffset()
方法。这个方法会判断是否已经滑动完毕,如果未完毕,则根据时间和差值器获取当前滑动的位置。
View动画有一个大的缺陷,就是不具有交互性,所以View动画只能做普通的动画效果,要避免交互操作,但是优点是效率高,使用方便。
属性动画可用的动画类型:translationX
和translationY
(延X或Y轴移动),rotation
、rotationX
和rotationY
(围绕支点进行旋转),PrivotX
和PrivotY
(控制支点位置),alpha
(透明度),x
和y
(View的最终位置)。
可以通过添加get
和set
方法添加属性动画的类型,在set方法中使用requestLayout()
重新显示View。
ValueAnimator
不提供任何动画效果,它是一个数值发生器,用来产生一定的规律的数字,从而控制动画实现。
在使用组合动画AnimatorSet
动画时,需要先用到AnimatorSet
的play
方法,该方法builder,builder使用建造者模式构建,当中包括4个方法:
after(Animator animator)
:将现有动画插入到传入的动画之后执行
after(long delay)
:将现有动画延迟指定时间执行
before(Animator anim)
:将现有动画插入到指定动画前执行
with(Animator anim)
:将现有动画和传入动画同时执行
组合动画除了使用AnimatorSet
,还可以使用PropertyValuesHolder
,当然,我们得结合OnjectAnimator.ofPropertyValuesHolder
方法来使用,当然,这种方法动画只能同时进行。代码如下:
1 | PropertyValuesHolder holder1=PropertyValuesHolder.ofFloat("scaleX",1.0f,1,5f); |
除此之外,还可以使用xml来进行动画,并通过AnimatorInflater.loadAnimator
方法来加载动画。
View事件分发
一个Activity
对象包含一个Window对象,这个对象由PhoneWindow
来实现。PhoneWindow
将DecorView
作为整个应用窗口的根View
,而这个DecorView
又将屏幕分为两个区域,一个是TitleView
,另一个是ContentView
,而我们的布局都展示在ContentView
中。
事件分发的重要3个方法:
dispatchTouchEvent(MotionEvent ev)
————用来进行事件的分发
onInterceptTouchEvent(MotionEvent ev)
————用来进行事件的拦截(dispatchTouchEvent 方法中执行,在View中不存在,在Viewgroup中)
onTouchEvent(MotionEvent ev)
————用来处理点击事件
分发机制具体的流程:
点击事件产生后,事件先会传递给当前的Activity
,这会调用Activity
的dispatchTouchEvent
方法,具体的工作会交由PhoneWindow
来完成。然后PhoneWindow
再把工作交由DecorView
,之后再有DecorView
把事件处理交由根ViewGroup
。
ViewGroup
的dispatchTouchEvent
方法中,首先要先判断事件是否是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时,会调用ActivityThread
的hanldeLaunchActivity
来创建activity。创建时会调用到oncreate
方法,从而完成DecorView的创建。然后会执行handleResumeActivity
方法,在这个方法里面,会调用Activity的onResume
方法。随后会得到DecorView
、WindowManager
、ViewManager
,并且调用其实现类WindowManagerImpl
的addView
。在WindowManagerImpl
的addView
方法中,调用WindowManagerGlobal
的addView
方法,而在这个方法中创建ViewRootImpl
实例,并且调用setView()
方法,将DecorView
加载到Window中。
同时,ViewRootImpl
还会调用PerformTraveals
方法,使ViewTree
开始View的工作流程。该方法会依次执行performMeasure
、performLayout
、
performDraw
方法。
MeasureSpec
代表了32位的int值,其中高2位是SpecMode(测量模式)
,低30位是SpecSize(测量尺寸)
。
SpecMode
的3种模式:
UNSPECIFIED
:未指定模式,View想多大就多大,不会受父容器限制。
AT_MOST
:最大模式,对应于wrap_content属性,子View的最终大小是父View指定的SpecSize,并且最大不能大于这个值。
EXACTLY
:精确模式,对应于match_content属性,父容器测量出View所需要的大小,就是SpecSize的值。