Android App开发实战项目之仿手机QQ动感影集动画播放(附源码和演示视频 可直接使用)
创始人
2024-04-11 07:39:41
0

需要图片集和源码请点赞关注收藏后评论区留言~~~

动感影集就是只要用户添加一张图片,动感影集就能给每张图片渲染不同的动画效果,让原本静止的图片变得活泼起来,辅以各种精致的动画特效,营造一种赏心悦目的感觉。

一、需求描述

动感影集一边播放,一边穿插着其他动画特效,读者可前往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;
impoimport 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; // 每个动画的播放时长@Overrideprotected 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]);}// 在属性动画开始播放时触发@Overridepublic 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("正在播放马赛克动画");}}// 在属性动画结束播放时触发@Overridepublic 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); // 给集合动画设置动画事件监听器}// 在属性动画取消播放时触发@Overridepublic void onAnimationCancel(Animator animation) {}// 在属性动画重复播放时触发@Overridepublic void onAnimationRepeat(Animator animation) {}// 在补间动画开始播放时触发@Overridepublic void onAnimationStart(Animation animation) {if (animation.equals(translateAnim)) {tv_anim_title.setText("正在播放平移动画");} else if (animation.equals(setAnim)) {tv_anim_title.setText("正在播放集合动画");}}// 在补间动画结束播放时触发@Overridepublic 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());}}// 在补间动画重复播放时触发@Overridepublic void onAnimationRepeat(Animation animation) {}}

XML文件

创作不易 觉得有帮助请点赞关注收藏~~~

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...