android 动画技巧

Android动画与视觉特效技巧

android动画系统十分复杂,要写详细的包含所有知识点的教程可能会是一本厚厚的书。首先是支持的动画各类非常多,lottie,gif,opengl,补间,属性,逐帧等种技术,二是写法十分的灵活,加上各种特效,系统的学习和演练,没有几个月是不行的。

这是抛砖引玉,只介绍我使用中用到的某些方法。

一.简单的补间动画,

淡入淡出;

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="0.0"
    android:toAlpha="1.0"
    android:duration="2000" />
 

绽放;

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXScale="0.0"
    android:toXScale="1.0"
    android:fromYScale="0.0"
    android:toYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:duration="2000" />
移动;

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromYDelta="100%"
    android:toYDelta="0"
    android:duration="2000" />
 

使用十分简单:

Animation moveUp = AnimationUtils.loadAnimation(this, R.anim.move_up);
view.startAnimation(moveUp);
 

当然也可以代码实现:

透明度:

Animation fadeIn = new AlphaAnimation(0.0f, 1.0f); // 透明度从0到1变化
fadeIn.setDuration(2000); // 持续时间2秒
view.startAnimation(fadeIn);
缩放;

Animation scaleUp = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); // 在中心点缩放
scaleUp.setDuration(2000); // 持续时间2秒
view.startAnimation(scaleUp);
移动:

Animation moveUp = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0.0f); // 从底部移动到顶部
moveUp.setDuration(2000); // 持续时间2秒
view.startAnimation(moveUp);
 

使用xml定义和代码是等效的,当希望一个图形既移动,又绽放,就需要用到set,里面的属性android:ordering="sequentially"可以依次播放,together可以同时播放,

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:ordering="sequentially"
    tools:ignore="MissingDefaultResource">
    
    <translate
        android:duration="500"
        android:fromXDelta="0"
        android:toXDelta="100dp"
        android:interpolator="@android:anim/linear_interpolator" />

    <translate
        android:duration="500"
        android:fromXDelta="100dp"
        android:toXDelta="0" />

    <scale
        android:duration="500"
        android:fromYScale="1.0"
        android:toYScale="0.2"
        android:pivotY="50%"
        android:interpolator="@android:anim/overshoot_interpolator" />

    <scale
        android:duration="500"
        android:fromYScale="0.2"
        android:toYScale="1.0"
        android:pivotY="50%" />

</set>

代码实现:

AnimatorSet animatorSet = new AnimatorSet();

animatorSet.playTogether(translation, scaleX, scaleY, rotation);

animatorSet.setDuration(1000); // 设置总持续时间

animatorSet.start(); // 开始动画

也可以使用另一种顺次播放,with属性和play 设置的动画一起播放,比如可以把放缩和位移一起播放,after在play之后播放,before在play之前播放,这样可以设置三个动画组按顺序依次播放,像电影里的多幕局一样。这种设置很方便我们设置,C先播放,播放完成后A和B一起一播放,A,B播放完成后播放C,形成一起种循环,当然循环也是专门的属性控制。

playSequentially

play(animator).with(anotherAnimator).after(scaleY).before();

二。属性动画

属性动画是android后期加入,逐渐取代补间动画的位置,理论上属性动画可以实现补间的所有功能。属性动画把一个视图变大和位移后,点击面积和位置就变了,如果你不需要这个,你依然可以使用补间动画。两种动画功能差不多,但使用的资源类型不动,他们在项目的不同文件夹里,一个是anim,一个是animator,话错就无法播放

一个简简单单的属性动画,这是一个使用了set的组合动画,当然也可以分开并不使用set:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!--  插值器的使用:android:interpolator="@android:anim/bounce_interpolator"  -->
    <!--  X轴方向平移  -->
    <objectAnimator
        android:interpolator="@android:anim/bounce_interpolator"
        android:duration="2000"
        android:propertyName="translationX"
        android:valueFrom="-500"
        android:valueTo="0"
        android:valueType="floatType" />

    <!--  Y轴方向平移  -->
    <objectAnimator
        android:duration="2000"
        android:propertyName="translationY"
        android:valueFrom="-500"
        android:valueTo="0"
        android:valueType="floatType" />

    <!--  X轴横向拉伸  -->
    <objectAnimator
        android:duration="2000"
        android:propertyName="scaleX"
        android:valueFrom="1"
        android:valueTo="2"
        android:valueType="floatType" />

    <!--  Y轴纵向拉伸  -->
    <objectAnimator
        android:duration="2000"
        android:propertyName="scaleY"
        android:valueFrom="1"
        android:valueTo="2"
        android:valueType="floatType" />

    <!--  透明度  -->
    <objectAnimator
        android:duration="1500"
        android:propertyName="alpha"
        android:valueFrom="1"
        android:valueTo="0"
        android:valueType="floatType" />

    <!--  旋转  -->
    <objectAnimator
        android:duration="3000"
        android:propertyName="rotation"
        android:valueFrom="0"
        android:valueTo="360"
        android:valueType="floatType" />
</set>
 

使用:

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
animator.setTarget(view);
animator.start();

完全的代码实现,两者是等效的,这往往取决于团队的习惯,xml文件的动画资源可以使用一些工具实现,特别适合某些不懂编程但懂设计的人才,他们使用和设计好,再让开发导入,可以保持动画和最终的效果一致。编程里的一些复杂设计,往往是为了方便一些非专业程序员加入。

 
  • // 创建平移动画

  • ObjectAnimator translateAnim = ObjectAnimator.ofFloat(animatedButton, "translationX", 0f, 300f);

  • translateAnim.setDuration(800);

  • // 创建缩放动画

  • ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(animatedButton, "scaleX", 1f, 1.5f);

  • scaleAnim.setDuration(800);

  • // 创建AnimatorSet

  • AnimatorSet animatorSet = new AnimatorSet();

  • animatorSet.playTogether(translateAnim, scaleAnim); // 同时执行

  • // 设置动画重复

  • animatorSet.setRepeatCount(1); // 重复一次,总共执行两次

  • animatorSet.setRepeatMode(ObjectAnimator.REVERSE); // 在完成后反向执行

  • // 启动动画

  • animatorSet.start();

animator有很多灵活的属性用来控制动画,

android:duration    动画播放时长
android:propertyName    动画属性
android:valueFrom    动画初始值
android:valueTo    动画结束值
android:startOffset    动画激活延时
android:repeatCount    动画重复次数
android:repeatMode    动画重复模式,repeat和reverse分别对应正序和倒序
android:valueType    取值参数类型
android:interpolator    设置插值器

android:propertyName,android:repeatCount,android:repeatMode,android:interpolator我认为是最重要的,android:interpolator有很多可选参数,一般需要熟练掌握,起码知道,方便要资源

AccelerateDecelerateInterpolator    其变化开始和结束速率较慢,中间加速
AccelerateInterpolator    其变化开始速率较慢,后面加速
DecelerateInterpolator    其变化开始速率较快,后面减速
LinearInterpolator    其变化速率恒定
AnticipateInterpolator    沿着开始相反的方向先运行
OvershootInterpolator    结束后顺着结束的运行规律让然运行一段时间
AnticipateOvershootInterpolator    AnticipateInterpolator 和 OvershootInterpolator 的结合
BounceInterpolator    自由落体规律运动
CycleInterpolator    其速率为正弦曲线
LinearOutSlowInInterpolator    其变化先匀速再减速
FastOutSlowInInterpolator    其变化是先加速,然后减速
FastOutLinearInInterpolator    其变化先加速然后匀速,本质还是加速运动

android:propertyName也有参数;

translationXX轴方向平移
translationYY轴方向平移
scaleXX轴横向拉伸
scaleYY轴纵向拉伸
alpha透明度
rotation旋转

android:repeatCount可以使用整型数值或者ObjectAnimator.INFINITE无限循环,android:repeatMode可以保证你不必额外写动画复位代码。

注;

1.属性动画有一类特殊的动画,ValueAnimator,他能实现类似set的效果,需要根据需要自己修改view的属性,比如有节奏的修改背景色,形状,同时修改位移,大小,角度,透明度等值,实现完全的放飞。在onAnimationUpdate修改view的所有的属性,他只控制动画的进度,其他的全看自己需要,这非常重要,当你需要开发基础动画之外的动画时,比如图形圆变方,颜色黑变白等等,他能提供一种根据interpolator参数变化的平滑实现。

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);

anim.setDuration(300);

anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

float currentValue = (float) animation.getAnimatedValue();

Log.d("TAG", "cuurent value is " + currentValue);

}

});

anim.start();

2,另一个常用的大概是,这个可以实现与ValueAnimator类似的功能,但更简单,如下,实现了按时间修改背景,其他能做的还更多,他可以修改view的几乎所有的属性,比如textview打字机效果。

public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)  

如下:

PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", 60f, -60f, 40f, -40f, -20f, 20f, 10f, -10f, 0f);  

PropertyValuesHolder colorHolder = PropertyValuesHolder.ofInt("BackgroundColor", 0xffffffff, 0xffff00ff, 0xffffff00, 0xffffffff);  

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, rotationHolder, colorHolder);  

animator.setDuration(3000);  

animator.setInterpolator(new AccelerateInterpolator());  

animator.start();  

三。RenderScript、ScriptIntrinsicBlur、RenderEffect 可以高速的给view和图片添加高斯模糊和其他特殊效果,因为RenderScript 是 Android 提供的异构计算框架,能在 CPU/GPU/专用 DSP 上高效运行,所有比一般情况下自己写的效率要高。这是一个实现高斯模糊的例子。

public static Bitmap blurBitmapRS(Context context, Bitmap src, float radius) {
        // 1. 创建输出 Bitmap,参数与源 Bitmap 相同
        Bitmap outBitmap = src.copy(src.getConfig(), true);
        // 2. 创建 RenderScript 对象
        RenderScript rs = RenderScript.create(context);
        // 3. 创建 ScriptIntrinsicBlur
        ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        // 4. 创建输入、输出 Allocation
        Allocation allIn  = Allocation.createFromBitmap(rs, src);
        Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);
        // 5. 设置模糊半径
        blurScript.setRadius(radius);
        // 6. 设置输入
        blurScript.setInput(allIn);
        // 7. 执行脚本
        blurScript.forEach(allOut);
        // 8. 拷贝到输出 Bitmap
        allOut.copyTo(outBitmap);
        // 9. 释放资源
        allIn.destroy();
        allOut.destroy();
        blurScript.destroy();
        rs.destroy();
        return outBitmap;
    }

由于view可以转为bitmap,使得我们可以对整个view实现高速的高斯模糊效果

           // 获取原图 Bitmap
            Bitmap src = ((BitmapDrawable)ivOriginal.getDrawable()).getBitmap();
            Bitmap blurred;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                // 半径范围 0 < radius ≤ 25
                blurred = BlurUtils.blurBitmapRS(this, src, 20f);
            } else {
                // 兼容老系统,fallback
                blurred = BlurUtils.blurBitmapFast(src, 20);
            }
            ivBlurred.setImageBitmap(blurred);
                        
四。RuntimeShader,可以在图像特效中使用shader脚本,一个高级的动画或者图像编程中常用,相机需要适时处理的场合,可以高速的水波,火焰,等粒子效果

//dazzling.agsl

uniform shader u_texture;
uniform vec2 u_resolution;
 
vec4 main(vec2 coords) {
    vec4 tex = u_texture.eval(coords);
    vec2 normUV = coords / u_resolution;
    vec3 color = tex.rgb * vec3(normUV.x, normUV.y, 0.5);
    return vec4(color, 1.0);
}
 

private void applyRuntimeShader(View view, float[] resolution) {
        String shaderCode = StringUtils.loadString(this, "shaders/dazzling.agsl");
        RuntimeShader shader = new RuntimeShader(shaderCode);
        shader.setFloatUniform("u_resolution", resolution);
        RenderEffect effect = RenderEffect.createRuntimeShaderEffect(shader, "u_texture");
        view.setRenderEffect(effect);
    }

五;drawBitmapMesh是个很重要的函数,可以实现水波荡漾,红旗招展,翻页,吸入等效果,

public void drawBitmapMesh (Bitmap bitmap, 
                int meshWidth,int meshHeight, 
                float[] verts,int vertOffset, 
                int[] colors,int colorOffset,Paint paint)

六。shader有很多种效果

BitmapShader:Android BitmapShader类详解 ,位图平铺。
LinearGradient:Android LinearGradient类详解 ,线性渐变。
RadialGradient:Android RadialGradient类详解 ,圆形渐变。
SweepGradient:Android SweepGradient类详解 ,角度渐变。
ComposeGradient:Android ComposeShader类详解 ,组合效果。

使用:

 try {
            InputStream isImage = getAssets().open("test6.png");
            bitmap = BitmapFactory.decodeStream(isImage);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //创建5种shader的派生类对象,分别测试效果
        shaders[0] = new BitmapShader(bitmap,Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);
        shaders[1] = new LinearGradient(0f,0f,100f,100f,colors,null, Shader.TileMode.REPEAT);
        shaders[2] = new RadialGradient(100f,100f,80f,colors,null, Shader.TileMode.REPEAT);
        shaders[3] = new SweepGradient(160f,160f,colors,null);
        shaders[4] = new ComposeShader(shaders[1],shaders[2], PorterDuff.Mode.ADD);

 case R.id.btn1:myView.setShader(shaders[0]);break;
            case R.id.btn2:myView.setShader(shaders[1]);break;
            case R.id.btn3:myView.setShader(shaders[2]);break;
            case R.id.btn4:myView.setShader(shaders[3]);break;
            case R.id.btn5:myView.setShader(shaders[4]);break;

BitmapShader可以实现mask效果,

Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.original_image);
    Bitmap maskBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mask_image);

    BitmapShader shader = new BitmapShader(maskBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    Paint paint = new Paint();
paint.setShader(shader);

    Canvas canvas = new Canvas(originalBitmap);
canvas.drawPaint(paint);

imageView.setImageBitmap(originalBitmap);

注:

view,drawable,bitmap是可以相互转化

             

七。setXfermode也可以实现mask

Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.original_image);
    Bitmap maskBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mask_image);

    Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

    Canvas canvas = new Canvas(originalBitmap);
canvas.drawBitmap(maskBitmap, 0, 0, null);
canvas.drawBitmap(originalBitmap, 0, 0, paint);

八。matrix

使用matrix也可以实现类似补间或者属性动画的效果,

       

参考:

Android--xml实现组合动画_android xml动画-优快云博客

Android属性动画完全解析(上),初识属性动画的基本用法-腾讯云开发者社区-腾讯云

Android自定义控件:动画类(九)----PropertyValuesHolder与Keyframe - vegatate - 博客园

Android实现Bitmap高斯模糊效果(附带源码)_android 高斯模糊-优快云博客

【Android】RuntimeShader 应用-优快云博客

使用Android Jetpack Compose渲染效果打造酷炫的动画效果_android agsl-优快云博客

https://www.jb51.net/article/38753.htm

Android APP完整基础教程(16)图形系统-图像特效_drawbitmapmesh-优快云博客

android mask切图_mob649e8164659f的技术博客_51CTO博客

android 画图之setXfermode_android setxmode-优快云博客

Android 中的图像特效(Matrix) - 银色的流星 - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值