View和动画,这些知识点你还记得吗?《Android题集》

·  阅读 3094

前言

上一篇Android题集整理了4大组件相关的题目,本篇主要是View和动画相关,希望能帮到大家巩固这些知识点,后续我会继续整理,欢迎关注和点赞。

目录

现在的目录
现在的目录

第一篇:四大组件,这些知识点你还记得吗?,点击可跳转

自定义View

自定义View有几种方式?

  • 组合现有控件
  • 继承现有控件
  • 继承View

Activity、Window、DecorView、ViewRoot之间的关系?

大概关系图 performTraversals流程图

更加详细的请看Carson_Ho大佬的:一篇文章带你完全梳理自定义View工作流程!

简述View的绘制流程?

  • measure: 判断是否需要重新计算 View 的大小,需要的话则计算。
  • layout: 判断是否需要重新计算 View 的位置,需要的话则计算。
  • draw: 判断是否需要重新绘制 View,需要的话则重绘制。

View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局和绘制,其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上。

绘制流程图
绘制流程图

MotionEvent是什么?

MotionEvent是手指接触屏幕后所产生的一系列事件。典型的事件类型有如下:

  • ACTION_DOWN:手指刚接触屏幕
  • ACTION_MOVE:手指在屏幕上移动
  • ACTION_UP:手指从屏幕上松开的一瞬间
  • ACTION_CANCELL:手指保持按下操作,并从当前控件转移到外层控件时触发

正常情况下,一次手指触摸屏幕的行为会触发一系列点击事件,考虑如下几种情况:

  • 点击屏幕后松开,事件序列:DOWN→UP
  • 点击屏幕滑动一会再松开,事件序列为DOWN->MOVE->.....→MOVE->UP

描述一下View事件传递分发机制

  • View事件分发本质就是对MotionEvent事件分发的过程。即当一个MotionEvent发生后,系统将这个点击事件传递到一个具体的View上
  • 点击事件的传递顺序:Activity(Window)→ViewGroup→ View
  • 事件分发过程由三个方法共同完成:
    • dispatchTouchEvent:用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件
    • onInterceptTouchEvent:在上述方法内部调用,对事件进行拦截。该方法只在ViewGroup中有,View(不包含 ViewGroup)是没有的。一旦拦截,则执行ViewGroup的onTouchEvent,在ViewGroup中处理事件,而不接着分发给View。且只调用一次,返回结果表示是否拦截当前事件
    • onTouchEvent: 在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件
事件分发流程,参考网上的图片,重画巩固记忆
事件分发流程,参考网上的图片,重画巩固记忆

如何解决View的事件冲突?举个开发中遇到的例子?

  • 常见开发中事件冲突的有ScrollView与RecyclerView的滑动冲突、RecyclerView内嵌同时滑动同一方向
  • 滑动冲突的处理规则:
    • 对于由于外部滑动和内部滑动方向不一致导致的滑动冲突,可以根据滑动的方向判断谁来拦截事件。
    • 对于由于外部滑动方向和内部滑动方向一致导致的滑动冲突,可以根据业务需求,规定何时让外部View拦截事件,何时由内部View拦截事件。
    • 对于上面两种情况的嵌套,相对复杂,可同样根据需求在业务上找到突破点。
  • 滑动冲突的实现方法:
    • 外部拦截法:指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,否则就不拦截。具体方法:需要重写父容器的onInterceptTouchEvent方法,在内部做出相应的拦截。
    • 内部拦截法:指父容器不拦截任何事件,而将所有的事件都传递给子容器,如果子容器需要此事件就直接消耗,否则就交由父容器进行处理。具体方法:需要配合requestDisallowInterceptTouchEvent方法。
事件拦截流程,参考网上的图片,重画巩固记忆
事件拦截流程,参考网上的图片,重画巩固记忆

scrollTo()和scollBy()的区别?

  • scollBy内部调用了scrollTo,它是基于当前位置的相对滑动;而scrollTo是绝对滑动,因此如果使用相同输入参数多次调用scrollTo方法,由于View的初始位置是不变的,所以只会出现一次View滚动的效果.
  • 两者都只能对View内容的滑动,而非使View本身滑动。可以使用Scroller有过度滑动的效果.

Scroller是怎么实现View的弹性滑动?

  • 在MotionEvent.ACTION_UP事件触发时调用startScroll()方法,该方法并没有进行实际的滑动操作,而是记录滑动相关量(滑动距离、滑动时间)
  • 接着调用invalidate/postInvalidate()方法,请求View重绘,导致View.draw方法被执行
  • 当View重绘后会在draw方法中调用computeScroll方法,而computeScroll又会去向Scroller获取当前的scrollX和scrollY;然后通过scrollTo方法实现滑动;接着又调用postInvalidate方法来进行第二次重绘,和之前流程一样,如此反复导致View不断进行小幅度的滑动,而多次的小幅度滑动就组成了弹性滑动,直到整个滑动过成结束

invalidate()和postInvalidate()的区别 ?

invalidate()与postInvalidate()都用于刷新View,主要区别是invalidate()在主线程中调用,若在子线程中使用需要配合handler;而postInvalidate()可在子线程中直接调用。

SurfaceView和View的区别?

  • View需要在UI线程对画面进行刷新,而SurfaceView可在子线程进行页面的刷新。
  • View适用于主动更新的情况,而SurfaceView适用于被动更新,如频繁刷新,这是因为如果使用View频繁刷新会阻塞主线程,导致界面卡顿。
  • SurfaceView在底层已实现双缓冲机制,而View没有,因此SurfaceView更适用于需要频繁刷新、刷新时数据处理量很大的页面(如视频播放界面)。

自定义View如何考虑机型适配 ?

  • 合理使用warp_content,match_parent
  • 针对不同的机型,使用不同的布局文件放在对应的目录下,android会自动匹配。
  • 尽量使用点9图片。
  • 使用与密度无关的像素单位dp,sp
  • 引入android的百分比布局。
  • 切图的时候切大分辨率的图,应用到布局当中。在小分辨率的手机上也会有很好的显示效果。

动画

Android中的动画有几种?

有帧动画,补间动画,属性动画三种。

帧动画

帧动画动画更多的依赖于完善的UI资源,他的原理就是将一张张单独的图片连贯的进行播放,从而在视觉上产生一种动画的效果;

作用在: View对象,比如TextView,但是不可以是背景,长度,颜色等。

testframe.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true" // 设置是否只播放一次,默认为false  >
    <item
        android:drawable="@drawable/a_0"
        android:duration="100" />
    <item
        android:drawable="@drawable/a_1"
        android:duration="100" />
    <item
        android:drawable="@drawable/a_2"
        android:duration="100" />
</animation-list>
复制代码

代码中:

     // 1. 设置动画
     iv.setImageResource(R.drawable.testframe);
     // 2. 获取动画对象
     animationDrawable = (AnimationDrawable) iv.getDrawable();
     // 3. 启动动画
     animationDrawable.start();
复制代码

使用时简单、方便,但是因为可能会使用大量 & 尺寸较大的图片资源,容易引起 OOM,所以要避免使用尺寸较大的图片。

补间动画

补间动画又可以分为四种形式,分别是AlphaAnimation(淡入淡出),TranslateAnimation(位移),ScaleAnimation(缩放大小),RotateAnimation(旋转)。 补间动画的实现,一般会采用xml文件的形式;代码会更容易书写和阅读,同时也更容易复用。Interpolator主要作用是可以控制动画的变化速率 ,就是动画进行的快慢节奏。pivot决定了当前动画执行的参考位置

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:shareInterpolator=["true" | "false"]
    // 以下参数是4种动画效果的公共属性,即都有的属性
    android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
    android:startOffset ="1000" // 动画延迟开始时间(ms)
    android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
    android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
    android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
    android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
    android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
    android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度
    >
    
  
    <alpha
        android:fromAlpha="float"// 动画开始时视图的透明度(取值范围: -1 ~ 1)
        android:toAlpha="float"  // 动画结束时视图的透明度(取值范围: -1 ~ 1) />
        
        
    <scale
        android:fromXScale="float"
        // 动画在水平方向X的起始缩放倍数
        // 0.0表示收缩到没有;1.0表示正常无伸缩
        // 值小于1.0表示收缩;值大于1.0表示放大
        android:toXScale="float" // 动画在水平方向X的结束缩放倍数
        android:fromYScale="float" // 动画开始前在竖直方向Y的起始缩放倍数
        android:toYScale="float" // 动画在竖直方向Y的结束缩放倍数
        android:pivotX="float" // 缩放轴点的x坐标
        android:pivotY="float" // 缩放轴点的y坐标  /> 
        
        
    <translate
        android:fromXDelta="float" // 视图在水平方向x 移动的起始值
        android:toXDelta="float" // 视图在水平方向x 移动的结束值
        android:fromYDelta="float" // 视图在竖直方向y 移动的起始值
        android:toYDelta="float" // 视图在竖直方向y 移动的结束值  />
        
        
    <rotate
        android:fromDegrees="float"// 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
        android:toDegrees="float"// 动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
        android:pivotX="float"// 旋转轴点的x坐标
        android:pivotY="float"// 旋转轴点的y坐标 />
        // 轴点 = 视图缩放的中心点
        // pivotX pivotY,可取值为数字,百分比,或者百分比p
        // 设置为数字时(如50),轴点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE。
        // 设置为百分比时(如50%),轴点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF。
        // 设置为百分比p时(如50%p),轴点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT
        
// 特别注意:
// 1. 在组合动画里scale缩放动画设置的repeatCount(重复播放)和fillBefore(播放完后,视图是否会停留在动画开始的状态)是无效的。
// 2. 所以如果需要重复播放或者回到原位的话需要在set标签里设置
// 3. 但是由于此处rotate旋转动画里已设置repeatCount为infinite,所以动画不会结束,也就看不到重播和回复原位


    <set>
        ...
    </set>
</set>
复制代码

属性动画

属性动画,顾名思义它是对于对象属性的动画。
作用于任意Java对象不再局限于视图View对象;可自定义各种动画效果,不再局限于4种基本变换:平移、旋转、缩放、透明度。

一般步骤:

  • 设置动画的运行时长,动画效果对应属性的初始值和结束值
  • 设置属性值从初始值过渡到结束值的变化逻辑
    • 设置值变化的模式趋势--插值器(可使用系统默认/自定义插值器-实现Interpolator接口)
    • 设置值变化的具体逻辑--估值器(可使用系统默认/自定义插值器-实现TypeEvaluator接口)
  • 根据变化逻辑不断改变值
  • 值每改变一次,就赋给对象的属性一次,有两种方式:
    • 手动赋值给对象属性--ValueAnimator类
    • 自动赋值给对象属性--ObjectAnimator类
  • 每次赋值调用invalidate()不断刷新视图(即调用了onDraw()重新绘制视图;每次绘制视图便实现了动画效果);如果初始值!=结束值就返回上一步,不断循环直到相等。

插值器(Interpolator)是什么?估值器(TypeEvaluator)用过没?

这两个都是实现动画效果中的一个辅助接口,但是估值器是属性动画才有的

插值器(Interpolator)

可以设置值变化的模式趋势,比如加速减速等;可以实现非线性运动的动画效果;
插值器决定值的变化模式,默认的种类有九个。 默认种类有:

  • AccelerateDecelerateInterpolator :在动画开始与结束的地方速率改变比较慢,在中间的时候加速
  • AccelerateInterpolator:在动画开始的地方速率改变比较慢,然后开始速率变化加快
  • LinearInterpolator:以常量速率改变
  • AnticipateInterpolator:开始的时候向后然后向前甩
  • CycleInterpolator:动画循环播放特定的次数,速率改变沿着正弦曲线
  • PathInterpolator:动画执行的效果按贝塞尔曲线
  • anticipateOvershootInterpolator:开始的时候向后然后向前甩一定值后返回最后的值
  • OvershootInterpolator:向前甩一定值后再回到原来位置
  • BounceInterpolator:动画结束的时候有弹起效果
  • 自定义插值器:
    • 写一个类实现Interpolator接口,Interpolator是一个空的接口继承了TimeInterpolator接口,定义getInterpolation方法

估值器(TypeEvaluator)

插值器决定了变化规律(加速减速等),而接下来的具体变化数值就是估值器的作用了;它可以协助插值器来实现非线性动画效果。
系统内置的估值器有3个:

  • IntEvaluator:以整型的形式从初始值 - 结束值 进行过渡
  • FloatEvaluator:以浮点型的形式从初始值 - 结束值 进行过渡
  • ArgbEvaluator:以Argb类型的形式从初始值 - 结束值 进行过渡 自定义估值器:
  • 自定义估值器需要实现 TypeEvaluator接口 & 复写evaluate()
public interface TypeEvaluator {  
    // 参数说明
    // fraction:插值器getInterpolation()的返回值
    // startValue:动画的初始值
    // endValue:动画的结束值
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        ....// 估值器的计算逻辑
        return xxx;
        // 赋给动画属性的具体数值
        // 使用反射机制改变属性变化
    }  
}  
复制代码

插值器的input值和估值器fraction有什么关系呢?

input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation(),然后通过实现getInterpolation()中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction了。

动画能组合在一起使用么?

可以将动画组合在一起使用AnimatorSet,
AnimatorSet.play() 播放当前动画的同时可以
.with() :将现有动画和传入的动画同时执行
.after() :将现有动画插入到传入的动画之后执行
.before() : 将现有动画插入到传入的动画之前执行

ObjectAnimator可以操作的属性(Property)?

  • Alpha 控制View的透明度
  • float TranslationX 控制X方向的位移
  • float TranslationY 控制Y方向的位移
  • float ScaleX 控制X方向的缩放倍数
  • float ScaleY 控制Y方向的缩放倍数
  • float Rotation 控制以屏幕方向为轴的旋转度数
  • float RotationX 控制以X轴为轴的旋转度数
  • float RotationY 控制以Y轴为轴的旋转度数

动画占用大量内存时,如何优化?

  • OOM问题:这个问题主要出现在帧动画中,当图片数量较多且图片较大时就极易出现OOM,这个在实际开发中要尤其注意,尽量避免使用帧动画。
  • 内存泄露:在属性动画中有一类无限循环的动画,这类动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄露,通过验证后发现View动画并不存在此问题。
  • View动画的问题:View动画是对View的影像做动画,并不是真正改变View的状态,因此有时候会出现动画完成后View无法隐藏的现象,即setVisibility(View.GOEN)失效了,这个时候只要调用view.clearAnimation()清除View动画即可解决问题。
  • 不要使用px:在进行动画的过程中,要尽量使用dp,使用px会导致在不用的设备上有不用的效果。
  • 动画元素的交互:从3.0开始,将view移动(平移)后,属性动画的单击事件触发位置为移动后的位置,但是View动画仍然在原位置。在Android3.0以前的系统中,不管是View动画还是属性动画,新位置都无法触发单击事件同时,老位置仍然能触发单击事件(因为属性动画在Android3.0以前是没有的,是通过兼容包实现的,底层也是调用View动画)

小结

文章中有不对的地方,可以提出来改正,希望能帮助到大家去复习这些知识点,后续会继续更新,欢迎点赞和关注。

参考文章
https://juejin.cn/post/6844904191224201229 https://www.jianshu.com/p/ab5785f017b2 https://blog.csdn.net/chuhe1989/article/details/104848602

分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改