小说网站 搜小说 无限网 烟雨红尘 小说爱好者 免费小说 免费小说网站

Android Studio精彩案例(六)《使用一个Demo涵盖补间动画所有知识》

转载本专栏文章,请注明出处,尊重原创 。文章博客地址:道龙的博客

元旦假期里,闲的无事,看到美团加载数据的动画,就突想写个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 = new Intent(this,Setup3Activity,class);
    startActivity(intent);
    finish();
}
那么我们又要想了,所有启动下一个活动都要重复写代码,反过来启动上一个活动也要写类似的Intent....这样的代码,因而切换活动可以写在基类里面。怎么实现呢?

在基类里面定义一个方法就好了,它的功能用于专门启动任意一个活动:

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文章。

微信公众号图片:





posted on 2017-01-02 21:14  王小航  阅读(1081)  评论(0编辑  收藏  举报

导航