Android Animation实战之一个APP的ListView的动画效果

时间:2021-10-30 21:01:45

熟悉了基础动画的实现后,便可以试着去实现常见app中出现过的那些精美的动画。今天我主要给大家引入一个app的listview的动画效果: 当展示listview时,listview的每一个列表项都按照规定的动画显示出来。
说起来比较抽象,先给大家看一个动画效果,这是app窝牛装修的listview显示动画:

   Android Animation实战之一个APP的ListView的动画效果

有木有觉得很酷炫?有木有啊!?

一、layout animation
    所谓的布局动画,其实就是为viewgroup添加显示动画效果,主要用过layoutanimationcontroller来控制实现。layoutanimationcontroller用于为一个layout里面的控件,或者是一个viewgroup里面的控件设置动画效果,可以在xml文件中设置,亦可以在java代码中设置。

1.1 在xml文件中设置布局动画

  首先,我们在res/anim文件夹下建立一个list_anim_layout.xml文件,该文件就是布局动画控制器。

?
1
2
3
4
<layoutanimation xmlns:android="http://schemas.android.com/apk/res/android"
  android:delay="30%"
  android:animationorder="random"
  android:animation="@anim/slide_right" />

android:delay  子类动画时间间隔 (延迟)   70% 也可以是一个浮点数 如“1.2”等。
android:animationorder="random"   子类的显示方式 random表示随机。
android:animationorder 的取值有 

  •      normal 0    默认
  •      reverse 1 倒序
  •      random 2   随机

android:animation="@anim/slide_right" 表示列表项显示时的具体动画是什么!
下面,我们定义每一个列表项显示时的动画效果吧,及slide_right.xml:

?
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
  android:interpolator="@android:anim/accelerate_interpolator">
 
  <translate
    android:duration="3000"
    android:fromxdelta="100%p"
    android:toxdelta="0%p" />
</set>

显示的效果为listview第一次出现的时候为 item随机出现 每个item都是从右边的区域向左滑动到显示的地方。
接下来,你只需要把这个布局动画,指定到viewgroup上就好了:

?
1
2
3
4
5
6
7
<listview
    android:id="@+id/listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutanimation="@anim/list_anim_layout"
    >
</listview>

就这么简单就完成了,快来看下效果吧:

Android Animation实战之一个APP的ListView的动画效果

1.2 在java代码中实现布局动画
    在java代码中实现布局动画并无难度,只要熟悉几个api的使用即可。 关于动画的定义和上文一致,只是,你不需要再在把控制动画应用到listview中了,即android:layoutanimation="@anim/list_anim_layout"这行代码可以删除。
    接下来,需要在java代码中进行配置:

?
1
2
3
4
5
6
7
8
9
10
11
12
private void startlayoutanim() {
  //通过加载xml动画设置文件来创建一个animation对象;
  animation animation = animationutils.loadanimation(this, r.anim.slide_right);
  //得到一个layoutanimationcontroller对象;
  layoutanimationcontroller lac = new layoutanimationcontroller(animation);
  //设置控件显示的顺序;
  lac.setorder(layoutanimationcontroller.order_reverse);
  //设置控件显示间隔时间;
  lac.setdelay(1);
  //为listview设置layoutanimationcontroller属性;
  listview.setlayoutanimation(lac);
}

    观察下,效果和之前使用xml文件肯定是一致的了。    

    介绍到这里,可能有人会有疑问了,博文一开始介绍的“窝牛装修”的那种效果,是每一个列表项显示的时候才会显示动画。我们这个确实所有的列表项的动画一起都显示了,只是显示顺序不同而已。 通过我们这种方法,怎么可能会达到那种效果呢?
    确实,通过布局动画,没办法控制每一个item在加载时才显示动画。那该如何是好呢?
    兄弟们,换个思路吧,如果布局动画完成不了,何必不直接用简单的补间动画,再结合每个列表项的显示控制,来实现窝牛装修列表显示的效果呢? 
    那有人会问了,怎么知道每一个列表项何时才加载么?
    你难道忘了baseadapter的getview()方法了么?
    没错,每当一个列表项显示时,都会主动调用baseadaper的getview()方法。

二、仿窝牛装修list列表的动画效果
    首先,我们定义一个动画资源,该动画即是列表项显示时的动画:woniu_list_item.xml

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
  android:interpolator="@android:anim/accelerate_interpolator"
  >
  <!--  woniu list item animation  -->
 
  <translate
    android:duration="500"
    android:fromxdelta="0"
    android:fromydelta="100"
    android:toxdelta="0"
    android:toydelta="0" />
</set>

    该平移动画表示,从下往上,垂直平移100px,时间为500毫秒。
    接下来,我们需要在baseadapter的getview()方法里,去使用该动画:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import android.content.context;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import android.view.animation.animation;
import android.view.animation.animationutils;
import android.widget.baseadapter;
import android.widget.imageview;
import android.widget.textview;
 
 
import java.util.list;
 
public class woniulistadapter extends baseadapter {
 
  private context mcontext;
 
  private layoutinflater minflater;
 
  private list<woniusimple> mdatas;
 
  private animation animation;
 
  public woniulistadapter(context context, list<woniusimple> datas) {
    mcontext = context;
    minflater = layoutinflater.from(mcontext);
    mdatas = datas;
 
    animation = animationutils.loadanimation(mcontext, r.anim.woniu_list_item);
  }
 
  @override
  public int getcount() {
    return (mdatas != null ? mdatas.size() : 0);
  }
 
  @override
  public object getitem(int position) {
    return (mdatas != null ? mdatas.get(position) : null);
  }
 
  @override
  public long getitemid(int position) {
    return position;
  }
 
  @override
  public view getview(final int position, view convertview, viewgroup parent) {
    viewholder holder = null;
    int type = getitemviewtype(position);
    if (convertview == null) {
 
      // 下拉项布局
      convertview = minflater.inflate(r.layout.list_item_woniu, null);
 
      holder = new viewholder();
 
      holder.tem_img = (imageview) convertview.findviewbyid(r.id.tem_img);
      holder.text_name = (textview) convertview.findviewbyid(r.id.text_name);
      holder.text_name = (textview) convertview.findviewbyid(r.id.text_name);
 
      convertview.settag(holder);
 
    } else {
      holder = (viewholder) convertview.gettag();
    }
 
    convertview.startanimation(animation);
 
    final woniusimple materialsimple = mdatas.get(position);
 
    if (materialsimple != null) {
//      holder.tem_img.setimageresource(r.mipmap.assist_default_img);
//      holder.text_name.settext(materialsimple.name);
//      holder.text_mobile.settext(materialsimple.mobile);
    }
 
 
    return convertview;
  }
 
  class viewholder {
 
    imageview tem_img;
 
    textview text_name;
 
    textview text_mobile;
  }
 
}

    我们来简要分析应用动画的地方: 1、我们adapter的构造方法里加载了之前定义的动画,活的animation对象。 2、 我们在getview方法里,为convertview设置并启动animation,即convertview.startanimation(animation)。
    够简单吧,只是这么两行代码,就可以实现在加载每一个view item时启动动画效果。
    可是,我们发现,这并不是非常完美的实现,为啥这么说呢?
    因为你此刻往上滑动列表,会发现,已经加载过的item的动画还会再次启动执行一次。这个体验太糟糕了。为啥会出现这种情况啊?
    因为getview方法的调用时机会对动画产生影响。adapter中的getview方法,会在每一个item处于可见状态时调用,所以无论你上滑还是下滑,都会重复调用getview方法(这也是listview为啥在使用时要进行优化的地方)。
    所以,为了解决刚刚发生的问题,我们可以设置标识,进行判断,已经加载过的view的动画不再进行启动加载。
    完整的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.lnyp.layoutanimation;
 
import android.content.context;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import android.view.animation.animation;
import android.view.animation.animationutils;
import android.widget.baseadapter;
import android.widget.imageview;
import android.widget.textview;
 
 
import java.util.hashmap;
import java.util.list;
import java.util.map;
 
public class woniulistadapter extends baseadapter {
 
  private context mcontext;
 
  private layoutinflater minflater;
 
  private list<woniusimple> mdatas;
 
  private animation animation;
 
  private map<integer, boolean> isfrist;
 
  public woniulistadapter(context context, list<woniusimple> datas) {
    mcontext = context;
    minflater = layoutinflater.from(mcontext);
    mdatas = datas;
 
    animation = animationutils.loadanimation(mcontext, r.anim.woniu_list_item);
    isfrist = new hashmap<integer, boolean>();
  }
 
  @override
  public int getcount() {
    return (mdatas != null ? mdatas.size() : 0);
  }
 
  @override
  public object getitem(int position) {
    return (mdatas != null ? mdatas.get(position) : null);
  }
 
  @override
  public long getitemid(int position) {
    return position;
  }
 
  @override
  public view getview(final int position, view convertview, viewgroup parent) {
    viewholder holder = null;
    int type = getitemviewtype(position);
    if (convertview == null) {
 
      // 下拉项布局
      convertview = minflater.inflate(r.layout.list_item_woniu, null);
 
      holder = new viewholder();
 
      holder.tem_img = (imageview) convertview.findviewbyid(r.id.tem_img);
      holder.text_name = (textview) convertview.findviewbyid(r.id.text_name);
      holder.text_name = (textview) convertview.findviewbyid(r.id.text_name);
 
      convertview.settag(holder);
 
    } else {
      holder = (viewholder) convertview.gettag();
    }
 
    // 如果是第一次加载该view,则使用动画
    if (isfrist.get(position) == null || isfrist.get(position)) {
 
      convertview.startanimation(animation);
      isfrist.put(position, false);
    }
 
    final woniusimple materialsimple = mdatas.get(position);
 
    if (materialsimple != null) {
//      holder.tem_img.setimageresource(r.mipmap.assist_default_img);
//      holder.text_name.settext(materialsimple.name);
//      holder.text_mobile.settext(materialsimple.mobile);
    }
 
 
    return convertview;
  }
 
  class viewholder {
 
    imageview tem_img;
 
    textview text_name;
 
    textview text_mobile;
  }
 
}

     看到了么,加了一个isfirst进行判断,这样,就可以有效控制动画的显示了。效果如下:

Android Animation实战之一个APP的ListView的动画效果

     本文我主要介绍了两个部分,一个是layout animation布局动画,使用布局动画可以控制view groups中的每一个数据的显示动画; 还一个就是实战,仿“窝牛装修”listview滑动时每一个item滑动进入可见状态的动画效果。通过这两个动画示例,我相信可以帮助大家更好的处理动画,克服“动画恐惧症”。

希望本文所述对大家学习有所帮助,大家也会喜欢,小编一定会再接再厉,为大家分享更多精彩的文章。