需要图片集和源码请点赞关注收藏后评论区留言~~~
动感影集就是只要用户添加一张图片,动感影集就能给每张图片渲染不同的动画效果,让原本静止的图片变得活泼起来,辅以各种精致的动画特效,营造一种赏心悦目的感觉。
一、需求描述
动感影集一边播放,一边穿插着其他动画特效,读者可前往QQ,点击左上角的头像打开个人菜单页,选择菜单项的我的相册,打开相册页面,点击相册页右上角的工具箱按钮,其中就有动感影集,进行效果测试
二、功能分析
动感影集的目的是使用动画技术呈现前后照片的动态切换效果,用到的动画必须承上启下,而且要求具备一定的视觉美感。效果包括以下几种
淡入淡出动画
灰度动画
平移动画
缩放动画
旋转动画
裁剪动画
集合动画
属性动画组合
其余动画 如百叶窗 马赛克等等
除了以上列举的动画技术,还需要考虑前后动画之间的无缝衔接,像补间动画可通过监听器AnimationListener侦听到播放完成事件,属性动画也是如此。但是对于淡入淡出动画而言,它属于图形类型,并非动画类型。因此无法通过动画事件的侦听来判断是否已经播放完成,只能利用处理器固定延迟一段时间开启下一个动画任务
动感影集的实现步骤主要包含以下三个步骤
1:编写动感影集刚开始的初始化代码
2:编写各种动画效果之间的承上启下衔接代码
3:编写动感影集末尾的集合动画代码
三、效果演示
演示视频如下 点击运行按钮后便会自动播放
动感影集
四、代码
Java类
package com.example.animation; import com.example.animation.widget.MosaicView; import com.example.animation.widget.ShutterView; import android.animation.Animator; import android.animation.RectEvaluator; import android.animation.Animator.AnimatorListener; import android.animation.ObjectAnimator; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PorterDuff; import android.graphics.Rect; impo import androidx.appcompat.app.AppCompatActivity; import android.os.Looper; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.RotateAnimation; import android.view.animation.ScaleAnimation; import android.view.animation.Animation.AnimationListener; import android.view.animation.TranslateAnimation; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.RelativeLayout; import android.widget.TextView; public class YingjiActivity extends AppCompatActivity implements AnimatorListener, AnimationListener { private RelativeLayout rl_yingji; // 声明一个相对布局对象 private TextView tv_anim_title; // 声明一个文本视图对象 private ImageView view1, view4, view5, view6; // 分别声明四个图像视图对象 private ShutterView view2; // 声明一个百叶窗视图对象 private MosaicView view3; // 声明一个马赛克视图对象 // 定义一个用于播放动感影集的风景照片资源数组 private int[] mImageArray = { R.drawable.bj01, R.drawable.bj02, R.drawable.bj03, R.drawable.bj04, R.drawable.bj05, R.drawable.bj06, R.drawable.bj07, R.drawable.bj08, R.drawable.bj09, R.drawable.bj10 }; private ObjectAnimator anim1, anim2, anim3, anim4; // 分别声明四个属性动画对象 private Animation translateAnim, setAnim; // 分别声明两个补间动画对象 private int mDuration = 5000; // 每个动画的播放时长 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_yingji); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 保持屏幕常亮 findViewById(R.id.iv_back).setOnClickListener(v -> finish()); TextView tv_title = findViewById(R.id.tv_title); tv_title.setText("动感影集"); rl_yingji = findViewById(R.id.rl_yingji); tv_anim_title = findViewById(R.id.tv_anim_title); playYingji(); // 开始播放动感影集 } // 开始 view1); // 往相对布局添加一个图像视图 // 构造一个在灰度上变化的属性动画 anim1 = ObjectAnimator.ofFloat(view1, "alpha", 0f, 1f); anim1.setDuration(mDuration); // 设置动画的播放时长 anim1.addListener(this); // 给属性动画添加动画事件监听器 anim1.start(); // 属性动画开始播放 } private ImageView getImageView(LayoutParams params, int imageId) { ImageView iv = new ImageView(this); iv.setLayoutParams(params); iv.setImageResource(imageId); iv.setScaleType(ScaleType.FIT_START); return iv; } // 初始化各视图 private void initView() { LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); view1 = getImageView(params, mImageArray[0]); view1.setAlpha(0f); // 设置视图的灰度 // 创建一个百叶窗视图 view2 = new ShutterView(this); view2.setLayoutParams(params); view2.setImageBitmap(BitmapFactory.decodeResource(getResources(), mImageArray[1])); view2.setMode(PorterDuff.Mode.DST_OUT); // 设置百叶窗视图的绘图模式 // 创建一个马赛克视图 view3 = new MosaicView(this); view3.setLayoutParams(params); view3.setImageBitmap(BitmapFactory.decodeResource(getResources(), mImageArray[2])); view3.setMode(PorterDuff.Mode.DST_OUT); // 设置马赛克视图的绘图模式 view3.setRatio(-5); view4 = getImageView(params, mImageArray[3]); view5 = getImageView(params, mImageArray[5]); view6 = getImageView(params, mImageArray[6]); } // 在属性动画开始播放时触发 @Override public void onAnimationStart(Animator animation) { if (animation.equals(anim1)) { tv_anim_title.setText("正在播放灰度动画"); } else if (animation.equals(anim2)) { tv_anim_title.setText("正在播放裁剪动画"); } else if (animation.equals(anim3)) { tv_anim_title.setText("正在播放百叶窗动画"); } else if (animation.equals(anim4)) { tv_anim_title.setText("正在播放马赛克动画"); } } // 在属性动画结束播放时触发 @Override public void onAnimationEnd(Animator animation) { if (animation.equals(anim1)) { // 灰度动画之后准备播放裁剪动画 rl_yingji.addView(view2, 0); // 从指定资源编号的图片文件中获取位图对象 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mImageArray[0]); int width = view1.getWidth(); int height = bitmap.getHeight() * width / bitmap.getWidth(); // 构造一个从四周向中间裁剪的属性动画 anim2 = ObjectAnimator.ofObject(view1, "clipBounds", new RectEvaluator(), new Rect(0, 0, width, height), new Rect(width / 2, height / 2, width / 2, height / 2)); anim2.setDuration(mDuration); // 设置动画的播放时长 anim2.addListener(this); // 给属性动画添加动画事件监听器 anim2.start(); // 属性动画开始播放 } else if (animation.equals(anim2)) { // 裁剪动画之后准备播放百叶窗动画 rl_yingji.removeView(view1); rl_yingji.addView(view3, 0); // 构造一个按比率逐步展开的属性动画 anim3 = ObjectAnimator.ofInt(view2, "ratio", 0, 100); anim3.setDuration(mDuration); // 设置动画的播放时长 anim3.addListener(this); // 给属性动画添加动画事件监听器 anim3.start(); // 属性动画开始播放 } else if (animation.equals(anim3)) { // 百叶窗动画之后准备播放马赛克动画 rl_yingji.removeView(view2); rl_yingji.addView(view4, 0); int offset = 5; view3.setOffset(offset); // 设置偏差比例 // 构造一个按比率逐步展开的属性动画 anim4 = ObjectAnimator.ofInt(view3, "ratio", 0 - offset, 101 + offset); anim4.setDuration(mDuration); // 设置动画的播放时长 anim4.addListener(this); // 给属性动画添加动画事件监听器 anim4.start(); // 属性动画开始播放 } else if (animation.equals(anim4)) { // 马赛克动画之后准备播放淡入淡出动画 rl_yingji.removeView(view3); // 淡入淡出动画需要先定义一个图形资源数组,用于变换图片 Drawable[] drawableArray = {getDrawable(mImageArray[3]), getDrawable(mImageArray[4])}; // 创建一个用于淡入淡出动画的过渡图形 TransitionDrawable td_fade = new TransitionDrawable(drawableArray); td_fade.setCrossFadeEnabled(true); // 是否启用交叉淡入 view4.setImageDrawable(td_fade); // 设置过渡图形 td_fade.startTransition(mDuration); // 开始过渡转换 tv_anim_title.setText("正在播放淡入淡出动画"); // 延迟若干秒后启动平移动画的播放任务。平移动画跟在淡入淡出动画后面 new Handler(Looper.myLooper()).postDelayed(() -> { rl_yingji.addView(view5, 0); // 创建一个平移动画 translateAnim = new TranslateAnimation(0f, -view4.getWidth(), 0f, 0f); translateAnim.setDuration(mDuration); // 设置动画的播放时长 translateAnim.setFillAfter(true); // 设置维持结束画面 view4.startAnimation(translateAnim); // 平移动画开始播放 translateAnim.setAnimationListener(this); // 给平移动画设置动画事件监听器 }, mDuration); } } // 开始播放集合动画 private void startSetAnim() { // 创建一个灰度动画 Animation alpha = new AlphaAnimation(1.0f, 0.1f); alpha.setDuration(mDuration); // 设置动画的播放时长 alpha.setFillAfter(true); // 设置维持结束画面 // 创建一个平移动画 Animation translate = new TranslateAnimation(1.0f, -200f, 1.0f, 1.0f); translate.setDuration(mDuration); // 设置动画的播放时长 translate.setFillAfter(true); // 设置维持结束画面 // 创建一个缩放动画 Animation scale = new ScaleAnimation(1.0f, 1.0f, 1.0f, 0.5f); scale.setDuration(mDuration); // 设置动画的播放时长 scale.setFillAfter(true); // 设置维持结束画面 // 创建一个旋转动画 Animation rotate = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotate.setDuration(mDuration); // 设置动画的播放时长 rotate.setFillAfter(true); // 设置维持结束画面 // 创建一个集合动画 setAnim = new AnimationSet(true); ((AnimationSet) setAnim).addAnimation(alpha); // 给集合动画添加灰度动画 ((AnimationSet) setAnim).addAnimation(translate); // 给集合动画添加平移动画 ((AnimationSet) setAnim).addAnimation(scale); // 给集合动画添加缩放动画 ((AnimationSet) setAnim).addAnimation(rotate); // 给集合动画添加旋转动画 setAnim.setFillAfter(true); // 设置维持结束画面 view5.startAnimation(setAnim); // 集合动画开始播放 setAnim.setAnimationListener(this); // 给集合动画设置动画事件监听器 } // 在属性动画取消播放时触发 @Override public void onAnimationCancel(Animator animation) {} // 在属性动画重复播放时触发 @Override public void onAnimationRepeat(Animator animation) {} // 在补间动画开始播放时触发 @Override public void onAnimationStart(Animation animation) { if (animation.equals(translateAnim)) { tv_anim_title.setText("正在播放平移动画"); } else if (animation.equals(setAnim)) { tv_anim_title.setText("正在播放集合动画"); } } // 在补间动画结束播放时触发 @Override public void onAnimationEnd(Animation animation) { if (animation.equals(translateAnim)) { rl_yingji.removeView(view4); rl_yingji.addView(view6, 0); startSetAnim(); // 开始播放集合动画 } else if (animation.equals(setAnim)) { rl_yingji.removeView(view5); tv_anim_title.setText("动感影集播放结束,谢谢观看"); view6.setOnClickListener(v -> playYingji()); } } // 在补间动画重复播放时触发 @Override public void onAnimationRepeat(Animation animation) {} }
XML文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <include layout="@layout/title_yingji" /> <TextView android:id="@+id/tv_anim_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="5dp" android:gravity="center" android:textColor="@color/black" android:textSize="17sp" /> <RelativeLayout android:id="@+id/rl_yingji" android:layout_width="match_parent" android:layout_height="400dp" android:background="@color/white" /> </LinearLayout>
创作不易 觉得有帮助请点赞关注收藏~~~