android几种常见的动画效果的实现

现在很多应用在页面里都使用了一些动画效果,前两天玩了玩人人和百合网客户端,实现了其中使用的几个动画效果。主要有以下几种:

1.      人人和百合登录页面动画。

这两款客户端登录页都有。首先看效果图。Gif做的比较烂,大家多发挥下想象力→_→。有兴趣的可以下载下这两个客户端体验一下,效果稍有不同,但大同小异。

貌似outlook不支持gif,所以放到附件里,见“登录动画”。

基本的效果是这样的,以百合网为例,两张图片,第一张图片逐渐放大,继而逐渐变小,变小的同时清晰度发生变化,逐渐模糊,类似淡入淡出的透明度变化效果。在第一张图逐渐透明的过程中,第二张图淡入,越来越清晰,同时逐渐放大。由于两张图左上角都有logo,所以还会有个logo有个影子叠在一起的感觉。第一张图消失之后,第二张图重复第一张图的渐变动画,消失时第一张图再次出现,如此反复。

实现这样的效果其实很简单,定义四种动画,放大,缩小,淡入,淡出。然后用AnimationSet组合。对动画实施监听,比如第一张图的消失动画,在动画开始时启动一个线程,此线程首先sleep一半的动画时间,然后启动第二张图的淡入动画,从而实现两张图的交互效果。第二张图调用第一张,第一张调用第二张,也很容易就达到了循环播放的效果。关键实现如下:

@Override

    protected void onCreate(Bundle savedInstanceState) {

       super.onCreate(savedInstanceState);

       setContentView(R.layout.layout_main);

       mImg = (ImageView) findViewById(R.id.img);

       mImg2 = (ImageView) findViewById(R.id.img2);

       mAniSet1 = new AnimationSet(true);

       mAniSet2 = new AnimationSet(true);

       mAniSet3 = new AnimationSet(true);

       mAniSet4 = new AnimationSet(true);

       mAniSet5 = new AnimationSet(true);

       ScaleAnimation sa1 = new ScaleAnimation(1.0F, 1.3F, 1.0F, 1.3F,

              Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF,

              0.5F);//放大动画

       ScaleAnimation sa2 = new ScaleAnimation(1.3F, 1.0F, 1.3F, 1.0F,

              Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF,

              0.5F); //缩小动画

       AlphaAnimation aa1 = new AlphaAnimation(1.0F, 0.0F); //淡出动画

       AlphaAnimation aa2 = new AlphaAnimation(0.0F, 1.0F); //淡入动画

       AlphaAnimation aa3 = new AlphaAnimation(0.1F, 1.0F);

 

       mAniSet5.addAnimation(aa1);

       mAniSet5.setDuration(8000L);

       mAniSet1.addAnimation(sa1);

       mAniSet1.addAnimation(aa3);

       mAniSet1.setDuration(8000L);

       mAniSet2.addAnimation(sa2);

       mAniSet2.addAnimation(aa1);

       mAniSet2.setDuration(8000L);

       mAniSet3.addAnimation(sa1);

       mAniSet3.addAnimation(aa2);

       mAniSet3.setDuration(8000L);

       mAniSet4.addAnimation(sa2);

       mAniSet4.addAnimation(aa1);

       mAniSet4.setDuration(8000L);

 

       mImg.startAnimation(mAniSet5); //首先一个淡出动画,动画结束时,切换图片,采用handle刷新页面。

 

       mAniSet5.setAnimationListener(new AnimationListener() {

 

           @Override

           public void onAnimationStart(Animation animation) {

              mImg.setBackgroundResource(R.drawable.index_bg_3);

           }

 

           @Override

           public void onAnimationEnd(Animation animation) {

              mImg.setBackgroundDrawable(null);

              new MyThread().start();

           }

 

           @Override

           public void onAnimationRepeat(Animation animation) {

              // TODO Auto-generated method stub

 

           }

 

       });

       mAniSet1.setAnimationListener(new AnimationListener() {

 

           @Override

           public void onAnimationStart(Animation animation) {

              mImg2.setBackgroundResource(R.drawable.index_bg_2);

 

           }

 

           @Override

           public void onAnimationRepeat(Animation animation) {

              // TODO Auto-generated method stub

 

           }

 

           @Override

           public void onAnimationEnd(Animation animation) {

              mImg2.startAnimation(mAniSet2);

 

           }

       });//动画逐渐放大,同时逐渐清晰,结束后启动下一个动画。

       mAniSet2.setAnimationListener(new AnimationListener() {

 

           @Override

           public void onAnimationEnd(Animation animation) {

              mImg2.setBackgroundDrawable(null);

 

           }

 

           @Override

           public void onAnimationRepeat(Animation animation) {

              // TODO Auto-generated method stub

 

           }

 

           @Override

           public void onAnimationStart(Animation animation) {

              new MyThread2().start();

 

           }

 

       });//动画逐渐变小,清晰度越来越差,直至消失。动画开始时启动一个线程,用于启动下一个图片的动画。

       mAniSet3.setAnimationListener(new AnimationListener() {

 

           @Override

           public void onAnimationStart(Animation animation) {

              mImg.setBackgroundResource(R.drawable.index_bg_3);

 

           }

 

           @Override

           public void onAnimationRepeat(Animation animation) {

              // TODO Auto-generated method stub

 

           }

 

           @Override

           public void onAnimationEnd(Animation animation) {

              mImg.startAnimation(mAniSet4);

 

           }

       });

       mAniSet4.setAnimationListener(new AnimationListener() {

 

           @Override

           public void onAnimationStart(Animation animation) {

              new MyThread().start();

 

           }

 

           @Override

           public void onAnimationRepeat(Animation animation) {

              // TODO Auto-generated method stub

 

           }

 

           @Override

           public void onAnimationEnd(Animation animation) {

              mImg.setBackgroundDrawable(null);

 

           }

       });

 

       mBtnEnter = (Button) findViewById(R.id.btnEnter);

       mBtnEnter.setOnClickListener(this);

    }

 

    class MyThread extends Thread {

       @Override

       public void run() {

           try {

              if (mSleep) {

                  Thread.sleep(4000L);

              }

              mSleep = true;

              Message localMessage = new Message();

              localMessage.what = 102;

              mHandler.sendMessage(localMessage);

              return;

           } catch (Exception localInterruptedException) {

              localInterruptedException.printStackTrace();

           }

       }

    }

 

    class MyThread2 extends Thread {

       @Override

       public void run() {

           try {

              Thread.sleep(4000L);

              Message localMessage = new Message();

              localMessage.what = 101;

              mHandler.sendMessage(localMessage);

              return;

           } catch (Exception localInterruptedException) {

              localInterruptedException.printStackTrace();

           }

       }

    }

 

    @Override

    public boolean handleMessage(Message msg) {

       switch (msg.what) {

       case 102:

           mImg2.startAnimation(mAniSet1);

           break;

       case 101:

           mImg.startAnimation(mAniSet3);

           break;

       }

       return false;

    }

 

2.      百合网个人信息页的抽屉效果。

动画效果见附件“抽屉动画”。

简单描述一下,就是点击一个按钮,按钮下方出来内容,高度逐渐变大,好像一个抽屉拉出来一样。再次点击,下方内容收起,好像抽屉拉上一般。为了让大家看清楚,gif每帧的时间很长,所以一顿一顿的,实际很流畅。

很多应用都有这个效果。我采用了三种方式实现。

第一种使用提供的动画TranslateAnimation,这种实现方式很简单,代码如下:

mAniSet = new AnimationSet(true);

       Animation showAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF,

              0.0f, Animation.RELATIVE_TO_SELF, 0.0f,

              Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF,

              0.0f);

       mAniSet.addAnimation(showAni);

       mAniSet.setDuration(600);

 

       mAniSet2 = new AnimationSet(true);

       Animation closeAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF,

              0.0f, Animation.RELATIVE_TO_SELF, 0.0f,

              Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF,

              -1.0f);

       mAniSet2.addAnimation(closeAni);

       mAniSet2.setDuration(600);

       mAniSet2.setAnimationListener(new AnimationListener() {

 

           @Override

           public void onAnimationStart(Animation animation) {

 

           }

 

           @Override

           public void onAnimationRepeat(Animation animation) {

 

           }

 

           @Override

           public void onAnimationEnd(Animation animation) {

              mBtnSlide

                      .setBackgroundResource(R.drawable.other_person_profile_detail_off);

              LayoutParams params1 = new LayoutParams(

                     LayoutParams.FILL_PARENT, 0);

              params1.height = 0;

              mTxtSlide.setLayoutParams(params1);

              mBtnSlide.setText("点击展开");

 

           }

       });

这种实现方式有一个弊端,就是下方抽屉的长度是固定的,实际上只是做了一个平移动画,因此启动动画的按钮下方如果有内容,那在动画开始时,下方的内容会立刻移动到动画结束时的位置,不能跟随上方的抽屉动画平滑移动。除非抽屉的高度一直在变化。于是想到了第二种办法,改变抽屉的长度。因为涉及到刷新页面,采用了一个AsyncTask,实现如下:

/**

     * 通过高度改变,实现抽屉效果,不流畅,不应sleep,且帧数过多。

     *

     * @author meng.chen

     *

     */

    private class showMenuTask extends AsyncTask<Boolean, Integer, Void> {

       private boolean mIsShow;

 

       @Override

       protected Void doInBackground(Boolean... params) {

           mIsShow = params[0];

           if (mIsShow) {

              for (int i = 0; i < 200; i++) {

                  publishProgress(i);

                  try {

                     Thread.sleep(1);

                  } catch (InterruptedException e) {

                     e.printStackTrace();

                  }

              }

           } else {

              for (int i = 200; i >= 0; i--) {

                  publishProgress(i);

                  try {

                     Thread.sleep(1);

                  } catch (InterruptedException e) {

                     e.printStackTrace();

                  }

              }

           }

 

           return null;

       }

 

       @Override

       protected void onProgressUpdate(Integer... values) {

           LayoutParams params1 = new LayoutParams(LayoutParams.FILL_PARENT, 0);

           params1.height = values[0];

           mTxtSlide.setLayoutParams(params1);

           if (values[0] <= 0 && !mIsShow) {

              mBtnSlide

                      .setBackgroundResource(R.drawable.other_person_profile_detail_off);

           }

           super.onProgressUpdate(values);

       }

 

       @Override

       protected void onPostExecute(Void result) {

 

           super.onPostExecute(result);

       }

 

    }

在onProgressUpdate中刷新页面,使用sleep方法控制动画时间。但是实际效果很不流畅,且刷新次数过多,比较耗资源。所以采用第三种方法,用handle刷新页面,采用timer定时刷新,帧数放大,整个动画一共也就几帧。动画效果也非常流畅。实现如下:

public boolean showHideAnimate(final Activity activity, final View view,

           final int height, int duration, final boolean show) {

       if (mTimer != null) {

           return false;

       }

       mTimer = new Timer();

       final int frame = 20;

       final Handler handler = new Handler() {

 

           @Override

           public void handleMessage(Message msg) {

              activity.runOnUiThread(new Runnable() {

                  public void run() {

                     int h = mTxtSlide.getHeight();

                     if (show) {

                         view.setVisibility(View.VISIBLE);

                         h += height / frame;

                         if (h >= height)

                            h = height;

                     } else {

                         h -= height / frame;

                         if (h <= 0) {

                            h = 0;

                            mBtnSlide

                                    .setBackgroundResource(R.drawable.other_person_profile_detail_off);

 

                         }

                     }

                     LinearLayout.LayoutParams params = (android.widget.LinearLayout.LayoutParams) view

                            .getLayoutParams();

                     LinearLayout.LayoutParams newParams = new LinearLayout.LayoutParams(

                            LayoutParams.WRAP_CONTENT, h);

                     newParams.bottomMargin = params.bottomMargin;

                     newParams.topMargin = params.topMargin;

                     view.setLayoutParams(newParams);

                     if (show) {

                         if (mTimer != null && h == height) {

                            mTimer.cancel();

                            mTimer = null;

                         }

                     } else {

                         if (mTimer != null && h == 0) {

                            mTimer.cancel();

                            mTimer = null;

                            view.setVisibility(View.GONE);

                         }

                     }

                  }

              });

              super.handleMessage(msg);

           }

       };

 

       mTimer.schedule(new TimerTask() {

           public void run() {

              handler.dispatchMessage(new Message());

           }

       }, 0, duration / frame);

       return true;

    }

 

3.      顶部浮层

动画效果见附件“顶部浮层”这个动画也是人人和百合都有。人人的稍微复杂一些。以百合为例,在其个人信息页面有个透明浮层,如gif中的“动画演示”。当页面内容过长时,可向上推动,当动画演示按钮滑动到页面顶部时,停止在页面顶端,其余内容继续向上滑动。人人网此效果体现在手机联系人页面,在“a-z”分组栏滑动到顶部时,固定在页面顶端,下一个分组过来,把之前的分组顶上去。

实现方案为准备两个按钮或布局,外观一模一样,实现同样的功能。一个在页面中的正常位置,如中间,一个在页面顶部,采用framelayout。初始只显示中间的控件,顶部的隐藏。当中间的控件滑动到顶部时,隐藏此控件,显示始终固定在顶部的控件。当上方内容逐渐滑下时,顶部控件隐藏,原中间的控件显示。代码实现如下:

mScroll = (ScrollView) findViewById(R.id.scroll);

       mScroll.setOnTouchListener(new OnTouchListener() {

 

           @Override

           public boolean onTouch(View v, MotionEvent event) {

              if (mScroll.getScrollY() >= mBtnScroll2Top.getTop()) {

                  mBtnTop.setVisibility(View.VISIBLE);

                  mBtnScroll2Top.setVisibility(View.INVISIBLE);

              } else {

                  mBtnTop.setVisibility(View.INVISIBLE);

                  mBtnScroll2Top.setVisibility(View.VISIBLE);

              }

              return false;

           }

       });

4.      页间切换动画

动画效果见附件“页面切换”。当切换页面时,目标页面从下方逐渐拉上来,原页面逐渐变暗消失。当目标页面返回时,目标页面再向着页面底端逐渐移出,原页面逐渐显示。很多app都有这个效果,人人客户端更是大量采用。实现非常简单,只需要一句代码。调用overridePendingTransition方法,需要传两个参数,第一个参数是目标页面的动画,第二个参数是原页面动画。代码实现如下:

anim_in_from_bottom.Xml

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android" >

 

<!--     <alpha

        android:duration="1000"

        android:fromAlpha="0.0"

        android:toAlpha="1.0" /> -->

 

    <translate

        android:duration="500"

        android:fromYDelta="100%"

        android:toYDelta="0" />

 

</set>

anim_disappear.xml

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android" >

 

    <alpha

        android:duration="500"

        android:fromAlpha="1.0"

        android:toAlpha="0.0" />

 

    <!-- <translate

        android:duration="1000"

        android:fromYDelta="0"

        android:toYDelta="-100%" />

 -->

</set><!--

 透明度控制动画效果 alpha

        浮点型值:

            fromAlpha 属性为动画起始时透明度

            toAlpha   属性为动画结束时透明度

            说明:

                0.0表示完全透明

                1.0表示完全不透明

            以上值取0.0-1.0之间的float数据类型的数字

       

        长整型值:

            duration  属性为动画持续时间

            说明:    

                时间以毫秒为单位

 

-->

调用代码,在startAcitivity方法或者finish方法之后调用。

。。。

startActivity(new Intent(mContext, TabPagerActivity.class));

           overridePendingTransition(R.anim.anim_in_from_bottom,

                  R.anim.anim_disappear);

。。。

5. viewpager

动画效果见附件“选项卡游标”。这个效果体现在tab下面的游标上,会跟着tab的切换而移动。Viewpager的应用非常广泛,主要用处有三个。一个是帮助页面。一个是优酷那种视频客户端在页面顶端的热门电影推荐,如

既定时切换,又可以手动滑动。一个是和tab页面结合。

帮助页面很简单,是最基本的viewpager的一个利用。第二种效果是viewpager和timer的结合。第三种和tab host的联合使用方式参照supportv4 demo中的实例,游标动画的实现代码如下:

@Override

       public void onPageSelected(int position) {

           mTabHost.setCurrentTab(position);

 

           Animation animation = null;

           switch (position) {

           case 0:

              if (mCurrentIndex == 1) {

                  animation = new TranslateAnimation(oneTabWidth, 0, 0, 0);

              } else if (mCurrentIndex == 2) {

                  animation = new TranslateAnimation(twoTabWidth, 0, 0, 0);

              }

              break;

           case 1:

              if (mCurrentIndex == 0) {

                  animation = new TranslateAnimation(mOffset, oneTabWidth, 0,

                         0);

              } else if (mCurrentIndex == 2) {

                  animation = new TranslateAnimation(twoTabWidth,

                         oneTabWidth, 0, 0);

              }

              break;

           case 2:

              if (mCurrentIndex == 0) {

                  animation = new TranslateAnimation(mOffset, twoTabWidth, 0,

                         0);

              } else if (mCurrentIndex == 1) {

                  animation = new TranslateAnimation(oneTabWidth,

                         twoTabWidth, 0, 0);

              }

              break;

           }

           mCurrentIndex = position;

           animation.setFillAfter(true);// True:图片停在动画结束位置

           animation.setDuration(300);

           mCursor.startAnimation(animation);

       }

。。。

 

 

完整的实现我已经上传到svn AndroidResearch\Project\AnimationTest目录下。


 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值