PropertyValuesHolder 组合动画
该动画无法实现前后关系,都是并行执行的。用法如下
val valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX",1.0f,1.5f) val valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX",0.0f,90.0f,0.0f) val valuesHolder3 = PropertyValuesHolder.ofFloat("alpha",1.0f,0.3f,1.0f) ObjectAnimator.ofPropertyValuesHolder(binding.coutomView,valuesHolder1,valuesHolder2,valuesHolder3).apply { duration = 2000 start() } 复制代码
使用属性动画
//aimator.scale.xml <?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="3000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="2.0" android:valueType="floatType"> </objectAnimator> 复制代码
AnimatorInflater.loadAnimator(this,R.animator.scale).apply { setTarget(binding.coutomView) start() } 复制代码
Scroller
public void smoothScrollTo(int destX,int destY){ int scrollX = getScrollX(); int delta = destX - scrollX; mScroller.startScroll(scrollX,0,delta,0,2000); invalidate(); } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()){ ((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); invalidate(); } } 复制代码
Scroller
并不能直接实现滑动,他最大的功能是在 startScroll()
处保存传入的滑动信息。后面再不断调用到 computeScroll()
这个方法,使用其中的 scrollTo()
来实现滑动。
computeScroll()
中,在判断方法中会调用到 mScroller.computeScrollOffset()
,这是用于获取 scrollX
和 scrollY
两个位置参数以及做出判断是否滑动结束。若是未滑动结束,就会让 computeScroll()
不断滑动重绘。
View事件分发
Activity构成图
Activity 的层级基本如上所示,在 xml
文件中构建的布局就是在 contentParent
位置,也就是 contentView
位置。
分发机制
首先需要了解的是 MotionEvent
,当屏幕被点击 ->产生点击事件 ->MotionEvent
产生。
点击事件产生后层层下发,不断传递到根 ViewGroup
。
事件分发的三大方法
dispatchTouchEvent(MotionEvent event)
: 用以事件分发。下面简称dTE()方法onInterceptTouchEvent(MotionEvent e)
: 用以拦截事件,在dispatchTouchEvent(MotionEvent event)
中被调用来拦截。该方法只有ViewGroup
中有,View
中没有onTouchEvent(MotionEvent e)
: 用以处理点击事件,在dispatchTouchEvent(MotionEvent event)
中被调用。这个方法是View
中的,但是由于ViewGroup
是继承自View
的,所以ViewGroup
可以使用。下面简称oTE()方法
下面简述一下 dispatchTouchEvent(MotionEvent event)
方法,
//ViewGroup内 public boolean dispatchTouchEvent(MotionEvent ev){ //拦截部分 ... onInterceptTouchEvent(ev); ... //点击处理事件 ... if(dispatchTransformedTouchEvent(ev,false,child,idBitsToAssign)) ... } private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child,int desiredPointerIdBits){ ... if(child == null){ handled = super.dispatchTouchEvent(event); }else{ handled = child..dispatchTouchEvent(event); } ... } //View内 public boolean dispatchTouchEvent(MotionEvent ev){ if(... && li.mOnTouchListener != null && li.mOnTouchListener.onTouch(this,event)){ result = true; } if(!result && onTouchEvent(event)){ result = true; } } 复制代码
拦截的处理逻辑
这其中的
允许拦截?
,一般通过子View的requestDisallowInterceptTouchEvent
来设置,这也是处理滑动冲突的方法之一。当事件在
ViewGroup
被拦截之后,后续的事件序列都交给其处理了
点击事件处理逻辑
事件被拦截后会被当前的
ViewGroup
处理,上图就是详细的点击事件处理流程图。
事件分发传递规则
View
的事件分发是,首先 View
层层分发下来,若是 onInterceptTouchEvent(ev)
为 true
就拦截,为 false
就继续下发。
当某一层级拦截后,就调用 onTouchEvent(event)
来处理,若是该层无法处理,就传递给父层的 onTouchEvent(event)
来处理。如此层层传递直到有对应可以处理的父层。
整个事件的分发过程看起来复杂,当最终归于三大方法可以用下面的伪代码表示
public boolean dispatchTouchEvent(MotionEvent ev){ boolean result = false; if(onInterceptTouchEvent(ev)){ result = onTouchEvent(ev); }else{ result = child.dispatchTouchEvent(ev); } return result; } 复制代码
总结
以上就是 View
体系的基础内容,理解 View
的事件分发原理,是我们能化用 View 的前提。View
处理事件层层下发的思想,是非常具有借鉴学习价值的,我们代码的设计也可以借鉴这套思想,提高代码的质量。