转载本专栏文章,请注明出处,尊重原创 。文章博客地址:道龙的博客
元旦假期里,闲的无事,看到美团加载数据的动画,就突想写个Demo把动画知识集成一下。后来想了想,还是直接用一个Demo来把所有动画知识穿插起来算了,该Demo涉及大多数动画应用场景。本篇案例,使用补间动画完成一个简单的动画功能集,会涉及多种形式的动画实现(即使补间动画很老套,但一些简单的动画功能还是可以选择它的)。其实在实际开发中也能看到这些影子。例如很多应用刚打开时候会有一些简单的动画效果,有的应用切换Activity的时候,也带有简单的平移动画效果更美观点,有的应用输入用户名密码错误会有抖动效果等等。同时会自定义View方式自定义一个进度条,模拟美团加载;模拟wifi链接网络;由于几个Activity功能差不多,我对其做一个简单的封装,抽取了BaseActivity。本案例,就是基于这些动画特性做的简化而形成的一个大Demo[界面丑陋,勿喷]。
GitHub下载源代码,打开代码传送门----------------->>>https://github.com/codeydl/App01
本文参考博客:参考博客1:Android 用Animation-list实现逐帧动画
参考博客2:(模拟美团客户端进度提示框)
先提前浏览一下Demo的效果图:
那么,咱们就从头慢慢来分析这个Demo吧,因为这属于老知识了,网上有很多详细的解析,而且比较简单,又有源代码可以下载,下面只给出核心代码和解析。
一、展示Splash动画
1、动画代码
这里使用旋转、缩放、渐变动画知识,启动Splash动画向导。一般实际项目中,可以在加载动画的时候,进行访问网络数据、拷贝本地数据库等耗时业务,而动画还可以起到对项目介绍的功能。
加载Splash我们可以使用下面代码完成:
//比例动画 ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAnimation.setDuration(2000); //渐变动画 AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1); alphaAnimation.setDuration(2000); //旋转动画 RotateAnimation rotateAnimation = new RotateAnimation(0,360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateAnimation.setDuration(2000); //动画集合,所有动画一起飞 AnimationSet animationSet = new AnimationSet(true); animationSet.addAnimation(scaleAnimation); animationSet.addAnimation(alphaAnimation); animationSet.addAnimation(rotateAnimation); //启动动画 mRl_splash_view.startAnimation(animationSet);使用动画集AnimationSet加载前述三种动画,然后使用一个View对象启动这个动画,把动画集合对象加载进去。
2、自定义的进度条
在播放动画的时候,我们在图片下方加上一个自定义进度条,这就用到自定义进度条的功能,本质上还是使用动画知识。自定义进度条,我们可以按照下面步骤完成:
1)、在res下新建anim文件,创建my_progress.xml
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/progess"> <!--自定义进度条,依托旋转动画。只需要引入一张图片即可。然后在需要使用该进度条的地方 ProgressBar位置下面。引入进度条:方式、android:indeterminateDrawable="@anim/my_progress"引入默认进度条 android:indeterminateDuration="800"修改旋转一圈耗费的时间--> </rotate>上边我们@drawable/给出一张图片就行了
然后在需要使用这个自定义进度条的地方引入就好了,引入方式上边已经注释。
2)、引入自定义进度条
<ProgressBar android:indeterminateDrawable="@anim/my_progress" android:indeterminateDuration="800"
3、界面切换
界面切换使用Handler延时两秒后,完成进入主界面操作
Handler发消息方
//两秒后发消息进入设置界面 mHandler.sendEmptyMessageDelayed(1,2000);然后Handler接收消息方代码:
private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: //进入Step1Activity Intent intent = new Intent(SplashActivity.this,Step1Activity.class); startActivity(intent); finish(); break; default: break; } } };
二、进入几个设置界面
由于几个设置界面只是为了展示切换Activity时平移的效果,几个功能大同小异。我把活动抽取了一下,在每个活动里面只需要简单的几行代码,就能完成效果。
1、先完成活动可切换
我们从第二个活动入手,说明为何要抽取基类。
第二个活动布局代码就两个按钮:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_step2_activiity" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:onClick="next" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:text="下一步" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:onClick="pre" android:layout_alignParentBottom="true" android:text="上一步" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>那么其他的活动与之类似,我们给Button设置了可点击事件。那么在每一个活动里面都会有同样的效果,即点击下一步动画切换下一个活动;点击上一步动画切换下一个活动。按钮事件通过next(View v){}方式实现,那么所有活动肯定有多余代码量,因此向上抽取同样功能,代码如下:
public void next(View v){ //启动下一个活动 nextActivity(); }以及:
public void pre(View v){ preActivity(); }以进入一下个活动为分析点:
我们发现调用了nextActivity();这个是一个抽象方法,让子类实现,点击下一步按钮,实际调用具体子类的该方法。因为,抽取了基类,还要实现下一步切换没有什么其他好的方式去实现。那么这个方法具体子类如何实现呢?
我们往往可能会直接重写该方法:
@Override protected void nextActivity() {那么我们又要想了,所有启动下一个活动都要重复写代码,反过来启动上一个活动也要写类似的Intent....这样的代码,因而切换活动可以写在基类里面。怎么实现呢?Intent intent = new Intent(this,Setup3Activity,class); startActivity(intent); finish();}
在基类里面定义一个方法就好了,它的功能用于专门启动任意一个活动:
public void startActivity(Class activity){ Intent intent = new Intent(this,activity); startActivity(intent); finish(); }子类如果想启动某一个活动,只需要重写上边的方法,并且传入要启动的活动.class就好了。比如我活动二中想启动Setup3Activity,以及启动Setup1Activity:
@Override protected void nextActivity() { startActivity(Step3Activity.class); } @Override protected void preActivity() { startActivity(Step1Activity.class); }是不是节省好多代码?哈哈,那是肯定的。完成了活动切换,然后考虑活动切换的动画切换
2、Activity动画切换效果写前分析:
1)、分析
最好的解释,莫过于图片。我用一张图片分析了切换的原理:
经过上图的分析,就可以给出切换下一个活动和切换上一个活动的动画xml了。以点击下一步时候需要的动画为出发点:
2)代码:
next_in.xml:
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="100%" android:toXDelta="0" android:duration="300" > </translate>next_out.xml:
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:fromYDelta="0" android:duration="300" android:toXDelta="-100%" android:toYDelta="0"> </translate>
3)、基类里面加入活动切换的代码:
使用API
overridePendingTransition();
由于动画切换也是每个活动所具备的,因此还可以写在基类里面:
public void nextAnimation(){ //加入动画方式启动下一个活动 overridePendingTransition(R.anim.next_in,R.anim.next_out); } public void preAnimation(){ //加入动画方式启动上一个活动 overridePendingTransition(R.anim.pre_in,R.anim.pre_out); }
只需要在基类点击方法next(View v){}和pre(View v){}里面调用一下就好了。
public void next(View v){ ................ //使用动画 nextAnimation(); }以及:
public void pre(View v){ .............. preAnimation(); }
以及在Setup3Activity进入主界面时候的动画(点击完成按钮,进入主界面):
public void complete(View v){ startActivity(MainActivity.class); overridePendingTransition(R.anim.bottom_in,R.anim.top_out); }
三、主活动界面布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_weight="1" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/et_activity_main_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入内容"/> <Button android:id="@+id/btn_activity_main_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="登录"/> </LinearLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="100dp"> <ImageView android:layout_centerHorizontal="true" android:id="@+id/animationIV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5px" android:src="@drawable/animation1"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true"> <Button android:id="@+id/buttonA" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:padding="5px" android:text="顺序显示"/> <Button android:id="@+id/buttonB" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:padding="5px" android:text="停止"/> <Button android:id="@+id/buttonC" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:padding="5px" android:text="倒序显示"/> </LinearLayout> </RelativeLayout> </LinearLayout>
这里第EditText与紧跟的button是一组。点击登录按钮,如果EditText无内容,就实现EditText抖动效果。如果有内容点击就弹出自定义对话框,实现模拟美团加载数据功能。最底下是一组,模拟wifi链接动画效果。
那么就一点点来实现出来吧:
1、监听EditText是否空,为空实现抖动效果:
抖动效果本质上其实就是平移动画,加入一个周期即可。
<translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="500" android:fromXDelta="0" android:interpolator="@anim/cycle_8" android:toXDelta="10" /> <!--平移10dp长度;interpolator动画插入器:表示动画循环多少次-->平移10个单位,500毫秒完成一个周期。引入一个周期:
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" android:cycles="8" /> <!--动画插入器-->在代码中点击button,抖动效果代码如下:
//抖动效果 Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.shake); etactivitymaincontent.startAnimation(animation);//给edittext加入动画
2、最底部完成wifi链接动画:
此时也是动画效果,使用多张动画,完成隔一段时间切换不同图片,达到动态效果。
方式如下:
在drawable下面,新建animation1(用于顺序动画)和animation2.(用于逆序动画)
只针对animation1做分析:
<?xml version="1.0" encoding="utf-8"?> <!-- 根标签为animation-list,其中oneshot代表着是否只展示一遍,设置为false会不停的循环播放动画 根标签下,通过item标签对动画中的每一个图片进行声明 android:duration 表示展示所用的该图片的时间长度 --> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true" > <item android:drawable="@drawable/icon1" android:duration="150"></item> <item android:drawable="@drawable/icon2" android:duration="150"></item> <item android:drawable="@drawable/icon3" android:duration="150"></item> <item android:drawable="@drawable/icon4" android:duration="150"></item> <item android:drawable="@drawable/icon5" android:duration="150"></item> <item android:drawable="@drawable/icon6" android:duration="150"></item> </animation-list>加入多张图片,并制定了动画展示时间。oneshot=true表示动画只播放一次。如果制定为fasle则不停的播放了。
那马在代码中,我们可以同过下面方式加入动画:
animationIV.setImageResource(R.drawable.animation1); mAnimationDrawable = (AnimationDrawable) animationIV.getDrawable(); mAnimationDrawable.start();把自定义动画xml作为参数设置到setImageResource中设置一个资源,再调用getDrawable方法返回值Drawable,注意这里强转为AnimationDrawable,只有这个对象才可以启动这种方式的动画。
当点击停止的时候:
mAnimationDrawable.stop();animation2原理一模一样。
3、模拟美团登录加载数据
1)、布局:
<ImageView android:id="@+id/iv_loading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/frame_meituan"/>主要说明一下imageview。他通过android:background="@drawable/frame_meituan"引入一个动画xml文件。这个文件原理跟上边的模拟wifi效果又是一样的了。动画效果也是两张图片不停地切换。
2)、自定义对话
模拟美团登录效果,其实也是两张动画的不停切换。原理和上边一样。只不过这里加入了自定义对话框功能:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initView(); initData(); } private void initData() { // 通过ImageView对象拿到背景显示的AnimationDrawable mAnimation = (AnimationDrawable) mIvLoading.getBackground(); //主界面点击登录,立即调用这里的动画显示功能 mAnimation.start(); //设置正在加载中信息 mTvLoading.setText(mLoadingTip); } private void initView() { //加载自定义布局,此时MyDialog已经有了该布局的样子 setContentView(R.layout.progress_dialog);// 显示界面 mTvLoading = (TextView) findViewById(R.id.tv_loading); mIvLoading = (ImageView) findViewById(R.id.iv_loading); }ProgressDialog不是ViewGroup,自定义布局通过setContentView方法加载进来自定义的布局。要重写onCreate,加入一个自定义布局,并且拿到布局中的实例。当外界初始化Dialog的时候,就会调用onCreate方法。初始化数据我们得到imageview的动画实例,这里同样强制转换为AnimationDrawable,只有这个对象才可以启动这种方式的动画。可见,只要外界一创建自定义对话框对象,就能启动该布局,而且启动动画,调用.show()方法,可以展示该自定义对话框。
当外界调用取消对话框的时候,我们要调用dismiss()方法。因而,我们需要在关闭的时候加入结束动画的代码:
@Override public void dismiss() { super.dismiss(); //对话框关闭,同时关闭掉动画。节约资源 mAnimation.stop(); mAnimation = null; }
构造方法,肯定是传入需要的上下文(这个上下文必须是Activity类型的),需要的设置的文本提示内容,如下:
public MyDialog(Context context,String content) { super(context); this.mContext = context; this.mLoadingTip = content; }
3)、引用该自定义的对话框。
//模拟美团登录 mDialog = new MyDialog(this, "正在加载中"); mDialog.show(); Handler handler =new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { mDialog.dismiss(); } }, 3000);//3秒钟后调用dismiss方法隐藏;如果有访问网络,可以设置访问网络成功后的监听,再停止对话框就像使用系统对话框一样,使用这个自定义对话框就好了。我们展示对话框后,并使用handler延时3秒关闭对话框。很简单。
上边实现步骤,我写的很详细,写了好久啊。看完记得加关注点赞啊~
最后,再看看实现的效果吧!
看完的朋友可以关注下,或者微信扫描下方二维码,关注公众号也可以:
打开微信搜索公众号 Android程序员开发指南 或者手机扫描下方二维码 在公众号阅读更多Android文章。
微信公众号图片: