本文目录

一、属性动画和MaterialDesign动画

属性动画和MaterialDesign动画相关的内容,已经在前面写过,具体查看https://www.jianshu.com/p/15d25638c001(属性动画)https://www.jianshu.com/p/a8f1f36c3fe0(MaterialDesign动画)

二、Android自定义动画框架

前面的部分,是动画的理论部分,这一部分,是动画的应用部分。
无论是封装一个自己的动画框架,还是封装一个自定义View,都需要遵从封装的原则:便于使用和复用,便于配置。

今天来做这样一个效果:

闪屏动画

略卡,是因为模拟器不好滑动的原因,在真机上的效果是非常流畅的,有点丑,个人能提供的设计有限,但跟那些绚丽的动画原理是一样的。

要封装成一个可以复用,便于配置的动画框架,如果只在xml文件中配置每个控件在X轴和Y轴上移动的加速度,就非常的方便。那么问题来了,我们在系统的控件上添加自定义属性,系统控件是无法识别的,但是如果在系统控件外包裹一层自定义的容器,让这个自定义的容器去识别这些自定义属性,这个问题就解决了。

那么我们现在就开始一步步的实现这样的效果。

1.分析页面,发现页面其实就是一个ViewPager,这里定义了一个ParallaxContainer继承自FrameLayout,在其中使用addView添加了一个ViewPager。其实也可以直接在Activity中写一个ViewPager,效果是一样的。

ParallaxContainer的代码如下

/*** Created by kimliu on 2018/12/25* 引导页最外层布局,里面有一个ViewPager*/
public class ParallaxContainer extends FrameLayout implements ViewPager.OnPageChangeListener{private ArrayList<ParallaxFragment> fragments;private ParallaxPagerAdapter adapter;private float containerWidth;private ImageView iv_main;public ViewPagerScrollListener mListener;private ViewPagerScrollListener listener;private void setmListener(ViewPagerScrollListener mListener){this.mListener = mListener;}public ParallaxContainer(@NonNull Context context,@Nullable AttributeSet attrs) {super(context, attrs);}/*** 初始化设置* @param childIds*/public void setUp(int...childIds){fragments = new ArrayList<>();for (int i = 0 ; i < childIds.length; i++){ParallaxFragment fragment = new ParallaxFragment();Bundle bundle = new Bundle();bundle.putInt("index",i);bundle.putInt("layoutId",childIds[I]);fragment.setArguments(bundle);fragments.add(fragment);}SplashActivity splashActivity = (SplashActivity) getContext();adapter = new ParallaxPagerAdapter(splashActivity.getSupportFragmentManager(),fragments);ViewPager viewPager = new ViewPager(getContext());viewPager.setId(R.id.parallax_pager);viewPager.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));viewPager.setAdapter(adapter);addView(viewPager);viewPager.addOnPageChangeListener(this);}/**** @param position 位置* @param positionOffset 移动偏移量* @param positionOffsetPixels 移动偏移量像素值*/@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {containerWidth = getWidth();//进入的页面ParallaxFragment inFragment = null;try {inFragment = fragments.get(position - 1);}catch (Exception e){//如果报错,那么inFragment为null,下面的ifelse就不会走}ParallaxFragment outFragment = null;try {outFragment = fragments.get(position);}catch (Exception e){//如果报错,那么outFragment为空,下面的ifelse就不会走}try {if(position != adapter.getCount() - 1) {//这里需要控制一下,因为最后一页是不需要设置动画,没有使用ViewContent包裹。if (inFragment != null) {//获取到的是什么呢?获得的是Fragment中的最外层ViewView view = inFragment.getView();ViewContent viewContent = (ViewContent) view;for (int i = 0; i < viewContent.getChildCount(); i++) {//拿到其中的View们,对其进行动画的控制View child = viewContent.getChildAt(i);if (!(child instanceof ViewPagerScrollListener)) {continue;}listener = (ViewPagerScrollListener) child;listener.onViewScrollIn(positionOffsetPixels, containerWidth);}}if (outFragment != null) {//拿到其中的View们 进行动画的控制View view = outFragment.getView();ViewContent viewContent = (ViewContent) view;for (int i = 0; i < viewContent.getChildCount(); i++) {View child = viewContent.getChildAt(i);if (!(child instanceof ViewPagerScrollListener)) {continue;}//AnimationView继承了ViewPagerScrollListenerlistener = (ViewPagerScrollListener) child;listener.onViewScrollOut(positionOffsetPixels);}}}}catch (Exception e){}}@Overridepublic void onPageSelected(int position) {if(position == adapter.getCount() - 1){iv_main.setVisibility(INVISIBLE);}else{iv_main.setVisibility(VISIBLE);}}@Overridepublic void onPageScrollStateChanged(int state) {//在滚动的状态下 动画开始 滚动停止 动画停止AnimationDrawable animationDrawable = (AnimationDrawable) iv_main.getBackground();switch (state){case ViewPager.SCROLL_STATE_DRAGGING:animationDrawable.start();break;case ViewPager.SCROLL_STATE_IDLE:animationDrawable.stop();break;}}/*** 拿到外层的ImageView,在这里需要根据ViewPager的滑动进行ImageView的动画控制,* 也可以使用接口回调的方式进行。但这里只是进行一个动画的开始和停止,不需要那么复杂* @param iv_main 外层ImageView:走路的小姑娘*/public void setIv_main(ImageView iv_main) {this.iv_main = iv_main;}
}

亮点:

在获取ViewPager中滑进去的页面和滑出来的页面时,使用了trycatch,如果出错,那么获取到的页面为空,就不会进行下面的操作。

2. ViewPager中的六个页面,前五个是要给其中的View设置动画的,需要设置动画的页面,我们需要使用一个自定义的容器ViewContent包裹,这个ViewContent的作用就是,遍历其中的View,如果用户传了自定义属性,那么就在这个View的外面包裹一层自定义容器,让这个自定义容器去识别用户传的自定义属性。

ViewContent的代码如下:

/*** Created by kimliu on 2018/12/27* 给View的外层包裹一个自定义的RelativeLayout* 遍历其中的View,如果添加了自定义属性,就在这个View外层包裹一个自定义容器*/
public class ViewContent extends RelativeLayout{private static final String TAG = ViewContent.class.getSimpleName();public ViewContent(Context context, AttributeSet attrs) {super(context, attrs);}/*** 查看源码发现,我们自定义View的时候,创建LayoutParams是调用的这个方法,那么我们重写这个方法,就可以偷梁换柱把LayoutParams换成我们自己LayoutParams* @param attrs* @return*/@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new MyLayoutParams(getContext(),attrs);}@Overridepublic void addView(View child, int index, ViewGroup.LayoutParams params) {MyLayoutParams p  = (MyLayoutParams) params;if(!isContent(p)){//如果没有传自定义属性,直接把系统的child添加到Relativelayout中super.addView(child,index,params);}else{//如果没有传自定义属性,在外层包裹一个ViewAnimationView view = new AnimationView(getContext());view.setAlphaIn(p.alphaIn);view.setAlphaOut(p.alphaOut);view.setxIn(p.xIn);view.setxOut(p.xOut);view.setyIn(p.yIn);view.setyOut(p.yOut);//            Log.d(TAG, "addView: " + p.alphaIn +","+ p.alphaOut +","
//            +","+p.xIn +","+p.xOut +","+p.yIn +","+p.yOut);//把系统控件添加到AnimationView中view.addView(child);// 把包裹后的view添加到Relativelayout中super.addView(view, index, params);}}/*** 是否有自定义属性* @param params* @return*/private boolean isContent(MyLayoutParams params){return params.alphaIn != 0 ||params.alphaOut != 0||params.xIn != 0||params.xOut != 0||params.yIn != 0||params.yOut != 0;}/*** 自定义LayoutParams,在其中获取自定义属性*/public static class MyLayoutParams extends RelativeLayout.LayoutParams{public int index;public float xIn;public float xOut;public float yIn;public float yOut;public float alphaIn;public float alphaOut;public MyLayoutParams(Context c, AttributeSet attrs) {super(c, attrs);TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ParallaxAnimation);alphaIn = a.getFloat(R.styleable.ParallaxAnimation_a_in,0f);//进入动画的透明度alphaOut = a.getFloat(R.styleable.ParallaxAnimation_a_out,0f);//出去动画的透明度xIn = a.getFloat(R.styleable.ParallaxAnimation_x_in,0f);//X轴上进入动画的加速度xOut = a.getFloat(R.styleable.ParallaxAnimation_x_out,0f);//X轴上出去动画的加速度yIn = a.getFloat(R.styleable.ParallaxAnimation_y_in,0f);//Y轴上进入动画的加速度yOut = a.getFloat(R.styleable.ParallaxAnimation_y_out,0f);//Y轴上出去动画的加速度a.recycle();}}
}

亮点:

  1. 查看源码可以发现,系统的View创建LayoutParams时,调用的是generateLayoutParams这个方法,如果我们重写这个方法,改变它的返回值,就可以偷梁换柱,把系统的LayoutParams换成我们自己写的LayoutParams。
    2.在我们自己的LayoutParams类MyLayoutParams中,获取用户输入的自定义属性,在addView中判断,如果用户添加了自定义属性,我们就在该View的外层包裹一个自定义容器,再添加到ViewContent中,如果用户没有添加自定义属性,我们就直接把系统控件添加到ViewContent中,又是一个偷梁换柱。
3.AnimationView,这个自定义容器是包裹在系统View外面的ViewGroup,用来根据用户输入的自定义属性来控制View的动画。在这里,我们使用了接口回调,用来传递ViewPager滑动的像素值。
/*** Created by kimliu on 2018/12/27* 在定义了自定义属性的View外层包裹一个ViewGroup 这个ViewGroup用来识别自定义属性* 并且根据这些自定义属性去做相应的操作**/
public class AnimationView extends FrameLayout implements ViewPagerScrollListener{public float xIn;public float xOut;public float yIn;public float yOut;public float alphaIn;public float alphaOut;public AnimationView(@NonNull Context context) {super(context);}public AnimationView(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public void setAlphaIn(float alphaIn) {this.alphaIn = alphaIn;}public void setAlphaOut(float alphaOut) {this.alphaOut = alphaOut;}public void setxIn(float xIn) {this.xIn = xIn;}public void setxOut(float xOut) {this.xOut = xOut;}public void setyIn(float yIn) {this.yIn = yIn;}public void setyOut(float yOut) {this.yOut = yOut;}@Overridepublic void onViewScrollIn(float positionOffsetPixels, float containerWidth) {//实现属性动画ViewHelper.setTranslationX(this,(containerWidth - positionOffsetPixels) * xIn);ViewHelper.setTranslationY(this,(containerWidth - positionOffsetPixels) * yIn);}@Overridepublic void onViewScrollOut(float positionOffsetPixels) {//实现属性动画ViewHelper.setTranslationX(this,0 - positionOffsetPixels * xOut);ViewHelper.setTranslationY(this,0 - positionOffsetPixels * yOut);}@Overridepublic void resetView() {ViewHelper.setTranslationX(this,0);ViewHelper.setTranslationY(this,0);}
}
4. ViewPagerScrollListener,在其中实现了三个方法,分别是在页面滑入时调用的方法onViewScrollIn;页面滑出时调用的方法onViewScrollOut;页面看不见时讲所有View重置的方法resetView。
/*** Created by kimliu on 2018/12/27*/
public interface ViewPagerScrollListener {/*** 页面进入时调用* @param positionOffsetPixels ViewPager滑动的偏移量* @param containerWidth 最外层container的宽度*/void onViewScrollIn(float positionOffsetPixels,float containerWidth);/*** 页面滑出时调用* @param positionOffsetPixels ViewPager滑动的偏移量*/void onViewScrollOut(float positionOffsetPixels);/*** 重置View 当View滑出屏幕时,将View的属性重置*/void resetView();
}
5.Fragment的编写,这里要注意的是,在这里进行页面上所有View的重置。
public class ParallaxFragment extends Fragment {@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater,@Nullable ViewGroup container,@Nullable Bundle savedInstanceState) {Bundle bundle = getArguments();int layoutId = bundle.getInt("layoutId");int index = bundle.getInt("index");return LayoutInflater.from(getActivity()).inflate(layoutId,null);}/*** 页面看不见时,获取页面中所有的View,进行View的重置* @param isVisibleToUser*/@Overridepublic void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);View view = getView();if(view instanceof ViewContent){ViewContent viewContent = (ViewContent) view;for (int i = 0; i < viewContent.getChildCount() ; i++){View child = viewContent.getChildAt(i);if(child instanceof AnimationView){ViewPagerScrollListener listener = (ViewPagerScrollListener) child;listener.resetView();}}}}
}
6.Activity的编写:设置透明状态栏,这里使用了第三方工具类:
implementation 'com.blankj:utilcode:1.21.2'
public class SplashActivity extends AppCompatActivity {private ParallaxContainer parallax_container;private ImageView iv_splash;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_splash);initStatusBar();parallax_container = findViewById(R.id.parallax_container);parallax_container.setUp(new int[]{R.layout.view_intro_1,R.layout.view_intro_2,R.layout.view_intro_3,R.layout.view_intro_4,R.layout.view_intro_5,R.layout.view_login});iv_splash = findViewById(R.id.iv_splash);iv_splash.setBackgroundResource(R.drawable.splash_run);parallax_container.setIv_main(iv_splash);}/*** 沉浸式状态栏*/private void initStatusBar() {RelativeLayout splash_relativelayout = findViewById(R.id.splash_relativelayout);splash_relativelayout.setPadding(splash_relativelayout.getPaddingLeft(),splash_relativelayout.getPaddingTop()+ BarUtils.getStatusBarHeight(),splash_relativelayout.getPaddingRight(),splash_relativelayout.getPaddingBottom());//设置为透明BarUtils.setStatusBarAlpha(this,0);BarUtils.setStatusBarLightMode(this,true);}
}
7. 最后要说一句,在ViewPager的前五个页面最外层,都需要包裹一个ViewContent,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<com.kimliu.kimliucustomview.ui.view.customAnimation.ViewContentxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="fill_parent"android:layout_height="fill_parent" ><ImageViewandroid:id="@+id/iv_0"android:layout_width="103dp"android:layout_height="19dp"android:layout_centerInParent="true"android:src="@drawable/intro1_item_0"app:x_in="1.2"app:x_out="1.2"/><ImageViewandroid:id="@+id/iv_1"android:layout_width="181dp"android:layout_height="84dp"android:layout_alignParentLeft="true"android:layout_alignParentTop="true"android:layout_marginLeft="13dp"android:layout_marginTop="60dp"android:src="@drawable/intro1_item_1"app:x_in="0.8"app:x_out="0.8" /><ImageViewandroid:id="@+id/iv_2"android:layout_width="143dp"android:layout_height="58dp"android:layout_alignParentRight="true"android:layout_alignParentTop="true"android:layout_marginTop="109dp"android:src="@drawable/intro1_item_2"app:x_in="1.1"app:x_out="1.8" /><ImageViewandroid:id="@+id/iv_3"android:layout_width="48dp"android:layout_height="48dp"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:layout_marginRight="40dp"android:layout_marginBottom="185dp"android:src="@drawable/intro1_item_3"app:x_in="0.8"app:x_out="0.8"app:a_in="0.8"app:a_out="0.8" /><ImageViewandroid:id="@+id/iv_4"android:layout_width="fill_parent"android:layout_height="128dp"android:layout_alignParentBottom="true"android:layout_marginBottom="29dp"android:background="@drawable/intro1_item_4"app:a_in="0.8"app:a_out="0.8"app:x_in="0.8"app:x_out="0.8" /><ImageViewandroid:id="@+id/iv_5"android:layout_width="260dp"android:layout_height="18dp"android:layout_alignParentBottom="true"android:layout_alignParentLeft="true"android:layout_marginBottom="16dp"android:layout_marginLeft="15dp"android:src="@drawable/intro1_item_5"app:a_in="0.9"app:a_out="0.9"app:x_in="0.9"app:x_out="0.9" /><ImageViewandroid:id="@+id/iv_6"android:layout_width="24dp"android:layout_height="116dp"android:layout_alignParentBottom="true"android:layout_alignParentLeft="true"android:layout_marginBottom="35dp"android:layout_marginLeft="46dp"android:src="@drawable/intro1_item_6"app:x_in="0.6"app:x_out="0.6" /><ImageViewandroid:id="@+id/iv_7"android:layout_width="45dp"android:layout_height="40dp"android:layout_alignParentBottom="true"android:layout_alignParentLeft="true"android:layout_marginBottom="23dp"android:layout_marginLeft="76dp"android:src="@drawable/intro1_item_7"app:a_in="0.3"app:a_out="0.3"app:x_in="0.5"app:x_out="0.5" /></com.kimliu.kimliucustomview.ui.view.customAnimation.ViewContent>

动画的内容就差不多到这里了,最近买了玉刚哥的知识星球,上面推荐了一些专门讲动画的博客,暂时没有时间去看,等空下来看看,如果有亮点内容我会再添加进来。

如果喜欢我的内容,欢迎关注我的公众号,虽然内容不多,但都是精华。

平头哥写代码

如果觉得我的文章对你有用,点波关注,关注我的后续更新,是对我最大的鼓励和支持。

六、Android中的Animation与炫酷开场动画相关推荐

  1. unity 3D炫酷开场动画

    2015/07/07// ///by xbw /环境 unity 4.6.1/// 游戏之前播放一段炫酷的动画是不是很能增加吸引力: unity支持的视频格式有mov. mpg. mpeg. mp4. ...

  2. Android中使用SVG实现炫酷动画效果

    前言 SVG,即Scalable Vector Graphics 可伸缩矢量图形.这种图像格式在前端中已经使用的非常广泛,而在移动端的开发中,遇到一些复杂的自定义控件或者动画效果,我们就可以考虑使用S ...

  3. 如何利用 Android 自定义控件实现炫酷的动画?|CSDN 博文精选

    作者 | u012551350 本文精选自 CSDN 博客,已获作者授权 「知足常乐」,很多人不满足现状,各种折腾,往往舍本逐末,常乐才能少一分浮躁,多一分宁静.近期在笔者身上发生了许多事情,心态也发 ...

  4. android svg动画框架,Android实现炫酷SVG动画效果

    svg是目前十分流行的图像文件格式了,svg严格来说应该是一种开放标准的矢量图形语言,使用svg格式我们可以直接用代码来描绘图像,可以用任何文字处理工具打开svg图像,通过改变部分代码来使图像具有交互 ...

  5. android 气球动画,Android TV使用贝赛尔曲线制作炫酷的开场动画

    目录 前言 很多App启动的时候会用到炫酷的开场动画.Android TV端也一样,每一个不同的模块,产品经理都可能设计了不同的开场动画.对于这些复杂的开场动画,最重要的是学会拆分,只要拆分得当,就会 ...

  6. Linux中一些有用而炫酷的代码

    Linux中一些有用而炫酷的代码 文章目录 Linux中一些有用而炫酷的代码 一.cal 1.当月的日历 2.近三个月的日历 3.一年的日历 二.yes 三.htop 四.fortune 五.scre ...

  7. html中flash的简单动画效果,css实现快速炫酷抖动动画效果

    1.Animate.css简介 Animate.css是一个可在您的Web项目中使用的即用型跨浏览器动画库.非常适合强调,首页,滑块和引导注意的提示.它是一个来自国外的 CSS3 动画库,它预设了抖动 ...

  8. android中设置Animation 动画效果

    在 Android 中, Animation 动画效果的实现可以通过两种方式进行实现,一种是 tweened animation 渐变动画,另一种是 frame by frame animation ...

  9. android 天气动画,为app制作炫酷天气动画 – WeatherView

    WeatherView 从1.1.0版本开始这个库使用了一个不同的setter结构. WeatherView是一个为app制作一个炫酷天气动画的Android库. Setup Android Stud ...

最新文章

  1. 大型神经网络可能初具意识:OpenAI首席科学家引争议,众大佬吵成一团
  2. QQ聊天文字背影图片拉伸方法
  3. 华清实训的收获(人工智能的小广告和福利)
  4. Teams App 扫描二维码
  5. Network 黑暗爆炸 - 3732 倍增lca || Kruskal重构树
  6. 柱状图如何添加数字标签_分类堆叠柱状图顺序排列及其添加合适条块标签
  7. vscode中文设置不生效_vscode 无法设置中文怎么办
  8. Acer 4750 安装黑苹果_超详细安装黑苹果教程
  9. Semantic UI 之 下拉菜单 dropdown
  10. 【加法器】——模拟电路设计简单的二进制数加法器
  11. 【回眸】Lunix内核 网络编程
  12. Excel表格导入导出功能实现
  13. 自动聚焦技术(AF)
  14. 《Hexo: 从零开始编写自己的主题》3. 优化样式,设计自己的主题
  15. 狗东API接口,item_get - 获得JD商品详情
  16. Matlab从视频中提取图像,可以设定每多少秒提取1帧。
  17. Microsoft .NET Framework 3.5 sp1离线安装(DotNetFX35)
  18. AI软件写作实战测试:写作技巧 - 让智能写作助手变得更加智能化?
  19. Python基础----字符串填充的几种方法
  20. 数组中的最大值/最小值

热门文章

  1. 截屏软件在截屏时窗口变大问题解决
  2. JAVA中Set的基本用法
  3. Python小程序——屏幕尺子
  4. 再探MS-SAMR协议
  5. Android 10.0 关机对话框UI定制化开发(二)
  6. Floyd-poj1847
  7. Web前端开发,必须要了解的跨域解决方案
  8. ZYNQ-AXI总线简介
  9. Springboot 解决 @JSONField无效问题
  10. python高级爬虫笔记(4)