超好看的下拉刷新动画Android代码实现

时间:2021-11-23 15:10:12

最近看到了好多高端、大气、上档次的动画效果,如果给你的项目中加上这些动画,相信你的app一定很优秀,今天给大家分析一下来自yalantis的一个超好看的下拉刷新动画。

首先我们看一下效果如何:

超好看的下拉刷新动画Android代码实现

怎么样?是不是很高大上?接下来我们看一下代码:

一、首先我们需要自定义刷新的动态refreshview(也就是下拉时候的头)
1.初始化头所占用的dimens

?
1
2
3
4
5
private void initiatedimens() {
    mscreenwidth = mcontext.getresources().getdisplaymetrics().widthpixels;
    mjettopoffset = mparent.gettotaldragdistance() * 0.5f;
    mtop = -mparent.gettotaldragdistance();
  }

2.为头填充图片,设置图片的大小
分别为左边的云彩,右边的云彩,中间的云彩还有中间的飞机,飞机是带有动画的,下面会介绍飞机的动画

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void createbitmaps() {
    mleftclouds = bitmapfactory.decoderesource(mcontext.getresources(), r.drawable.clouds_left);
    mrightclouds = bitmapfactory.decoderesource(mcontext.getresources(), r.drawable.clouds_right);
    mfrontclouds = bitmapfactory.decoderesource(mcontext.getresources(), r.drawable.clouds_center);
    mjet = bitmapfactory.decoderesource(mcontext.getresources(), r.drawable.airplane);
 
    mjetwidthcenter = mjet.getwidth() / 2;
    mjetheightcenter = mjet.getheight() / 2;
    mfrontcloudwidthcenter = mfrontclouds.getwidth() / 2;
    mfrontcloudheightcenter = mfrontclouds.getheight() / 2;
 
    mrightcloudswidthcenter = mrightclouds.getwidth() / 2;
    mrightcloudsheightcenter = mrightclouds.getheight() / 2;
    mleftcloudswidthcenter = mleftclouds.getwidth() / 2;
    mleftcloudsheightcenter = mleftclouds.getheight() / 2;
  }

3.然后我们来画这个头

?
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
public void draw(@nonnull canvas canvas) {
    final int savecount = canvas.save();
 
    // draw background.
    canvas.drawcolor(mcontext.getresources().getcolor(r.color.sky_background));
 
    if (isrefreshing) {
      // set up new set of wind
      while (mwinds.size() < wind_set_amount) {
        float y = (float) (mparent.gettotaldragdistance() / (math.random() * random_y_coefficient));
        float x = random(min_wind_x_offset, max_wind_x_offset);
 
        // magic with checking interval between winds
        if (mwinds.size() > 1) {
          y = 0;
          while (y == 0) {
            float tmp = (float) (mparent.gettotaldragdistance() / (math.random() * random_y_coefficient));
 
            for (map.entry<float, float> wind : mwinds.entryset()) {
              // we want that interval will be greater than fifth part of draggable distance
              if (math.abs(wind.getkey() - tmp) > mparent.gettotaldragdistance() / random_y_coefficient) {
                y = tmp;
              } else {
                y = 0;
                break;
              }
            }
          }
        }
 
        mwinds.put(y, x);
        drawwind(canvas, y, x);
      }
 
      // draw current set of wind
      if (mwinds.size() >= wind_set_amount) {
        for (map.entry<float, float> wind : mwinds.entryset()) {
          drawwind(canvas, wind.getkey(), wind.getvalue());
        }
      }
 
      // we should to create new set of winds
      if (minversedirection && mnewwindset) {
        mwinds.clear();
        mnewwindset = false;
        mwindlinewidth = random(min_wind_line_width, max_wind_line_width);
      }
 
      // needed for checking direction
      mlastanimationtime = mloadinganimationtime;
    }
 
    drawjet(canvas);
    drawsideclouds(canvas);
    drawcenterclouds(canvas);
 
    canvas.restoretocount(savecount);
  }
?
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/**
   * draw wind on loading animation
   *
   * @param canvas - area where we will draw
   * @param y    - y position fot one of lines
   * @param xoffset - x offset for on of lines
   */
  private void drawwind(canvas canvas, float y, float xoffset) {
    /* we should multiply current animation time with this coefficient for taking all screen width in time
    removing slowing of animation with dividing on {@link #slow_down_animation_coefficient}
    and we should don't forget about distance that should "fly" line that depend on screen of device and x offset
    */
    float cof = (mscreenwidth + xoffset) / (loading_animation_coefficient / slow_down_animation_coefficient);
    float time = mloadinganimationtime;
 
    // horrible hack for revers animation that should work like restart animation
    if (mlastanimationtime - mloadinganimationtime > 0) {
      minversedirection = true;
      // take time from 0 to end of animation time
      time = (loading_animation_coefficient / slow_down_animation_coefficient) - mloadinganimationtime;
    } else {
      mnewwindset = true;
      minversedirection = false;
    }
 
    // taking current x position of drawing wind
    // for fully disappearing of line we should subtract wind line width
    float x = (mscreenwidth - (time * cof)) + xoffset - mwindlinewidth;
    float xend = x + mwindlinewidth;
 
    canvas.drawline(x, y, xend, y, mwindpaint);
  }
 
  private void drawsideclouds(canvas canvas) {
    matrix matrixleftclouds = mmatrix;
    matrix matrixrightclouds = madditionalmatrix;
    matrixleftclouds.reset();
    matrixrightclouds.reset();
 
    // drag percent will newer get more then 1 here
    float dragpercent = math.min(1f, math.abs(mpercent));
 
    boolean overdrag = false;
 
    // but we check here for overdrag
    if (mpercent > 1.0f) {
      overdrag = true;
    }
 
    float scale;
    float scalepercentdelta = dragpercent - scale_start_percent;
    if (scalepercentdelta > 0) {
      float scalepercent = scalepercentdelta / (1.0f - scale_start_percent);
      scale = side_clouds_initial_scale + (side_clouds_final_scale - side_clouds_initial_scale) * scalepercent;
    } else {
      scale = side_clouds_initial_scale;
    }
 
    // current y position of clouds
    float dragyoffset = mparent.gettotaldragdistance() * (1.0f - dragpercent);
 
    // position where clouds fully visible on screen and we should drag them with content of listview
    int cloudsvisibleposition = mparent.gettotaldragdistance() / 2 - mleftcloudsheightcenter;
 
    boolean needmovecloudswithcontent = false;
    if (dragyoffset < cloudsvisibleposition) {
      needmovecloudswithcontent = true;
    }
 
    float offsetrightx = mscreenwidth - mrightclouds.getwidth();
    float offsetrighty = (needmovecloudswithcontent
        ? mparent.gettotaldragdistance() * dragpercent - mleftclouds.getheight()
        : dragyoffset)
        + (overdrag ? mtop : 0);
 
    float offsetleftx = 0;
    float offsetlefty = (needmovecloudswithcontent
        ? mparent.gettotaldragdistance() * dragpercent - mleftclouds.getheight()
        : dragyoffset)
        + (overdrag ? mtop : 0);
 
    // magic with animation on loading process
    if (isrefreshing) {
      if (checkcurrentanimationpart(animationpart.first)) {
        offsetlefty += getanimationpartvalue(animationpart.first) / y_side_clouds_slow_down_cof;
        offsetrightx -= getanimationpartvalue(animationpart.first) / x_side_clouds_slow_down_cof;
      } else if (checkcurrentanimationpart(animationpart.second)) {
        offsetlefty += getanimationpartvalue(animationpart.second) / y_side_clouds_slow_down_cof;
        offsetrightx -= getanimationpartvalue(animationpart.second) / x_side_clouds_slow_down_cof;
      } else if (checkcurrentanimationpart(animationpart.third)) {
        offsetlefty -= getanimationpartvalue(animationpart.third) / y_side_clouds_slow_down_cof;
        offsetrightx += getanimationpartvalue(animationpart.third) / x_side_clouds_slow_down_cof;
      } else if (checkcurrentanimationpart(animationpart.fourth)) {
        offsetlefty -= getanimationpartvalue(animationpart.fourth) / x_side_clouds_slow_down_cof;
        offsetrightx += getanimationpartvalue(animationpart.fourth) / y_side_clouds_slow_down_cof;
      }
    }
 
    matrixrightclouds.postscale(scale, scale, mrightcloudswidthcenter, mrightcloudsheightcenter);
    matrixrightclouds.posttranslate(offsetrightx, offsetrighty);
 
    matrixleftclouds.postscale(scale, scale, mleftcloudswidthcenter, mleftcloudsheightcenter);
    matrixleftclouds.posttranslate(offsetleftx, offsetlefty);
 
    canvas.drawbitmap(mleftclouds, matrixleftclouds, null);
    canvas.drawbitmap(mrightclouds, matrixrightclouds, null);
  }
 
  private void drawcenterclouds(canvas canvas) {
    matrix matrix = mmatrix;
    matrix.reset();
    float dragpercent = math.min(1f, math.abs(mpercent));
 
    float scale;
    float overdragpercent = 0;
    boolean overdrag = false;
 
    if (mpercent > 1.0f) {
      overdrag = true;
      // here we want know about how mach percent of over drag we done
      overdragpercent = math.abs(1.0f - mpercent);
    }
 
    float scalepercentdelta = dragpercent - scale_start_percent;
    if (scalepercentdelta > 0) {
      float scalepercent = scalepercentdelta / (1.0f - scale_start_percent);
      scale = center_clouds_initial_scale + (center_clouds_final_scale - center_clouds_initial_scale) * scalepercent;
    } else {
      scale = center_clouds_initial_scale;
    }
 
    float parallaxpercent = 0;
    boolean parallax = false;
    // current y position of clouds
    float dragyoffset = mparent.gettotaldragdistance() * dragpercent;
    // position when should start parallax scrolling
    int startparallaxheight = mparent.gettotaldragdistance() - mfrontcloudheightcenter;
 
    if (dragyoffset > startparallaxheight) {
      parallax = true;
      parallaxpercent = dragyoffset - startparallaxheight;
    }
 
    float offsetx = (mscreenwidth / 2) - mfrontcloudwidthcenter;
    float offsety = dragyoffset
        - (parallax ? mfrontcloudheightcenter + parallaxpercent : mfrontcloudheightcenter)
        + (overdrag ? mtop : 0);
 
    float sx = overdrag ? scale + overdragpercent / 4 : scale;
    float sy = overdrag ? scale + overdragpercent / 2 : scale;
 
    if (isrefreshing && !overdrag) {
      if (checkcurrentanimationpart(animationpart.first)) {
        sx = scale - (getanimationpartvalue(animationpart.first) / loading_animation_coefficient) / 8;
      } else if (checkcurrentanimationpart(animationpart.second)) {
        sx = scale - (getanimationpartvalue(animationpart.second) / loading_animation_coefficient) / 8;
      } else if (checkcurrentanimationpart(animationpart.third)) {
        sx = scale + (getanimationpartvalue(animationpart.third) / loading_animation_coefficient) / 6;
      } else if (checkcurrentanimationpart(animationpart.fourth)) {
        sx = scale + (getanimationpartvalue(animationpart.fourth) / loading_animation_coefficient) / 6;
      }
      sy = sx;
    }
 
    matrix.postscale(sx, sy, mfrontcloudwidthcenter, mfrontcloudheightcenter);
    matrix.posttranslate(offsetx, offsety);
 
    canvas.drawbitmap(mfrontclouds, matrix, null);
  }
 
  private void drawjet(canvas canvas) {
    matrix matrix = mmatrix;
    matrix.reset();
 
    float dragpercent = mpercent;
    float rotateangle = 0;
 
    // check overdrag
    if (dragpercent > 1.0f && !mendofrefreshing) {
      rotateangle = (dragpercent % 1) * 10;
      dragpercent = 1.0f;
    }
 
    float offsetx = ((mscreenwidth * dragpercent) / 2) - mjetwidthcenter;
 
    float offsety = mjettopoffset
        + (mparent.gettotaldragdistance() / 2)
        * (1.0f - dragpercent)
        - mjetheightcenter;
 
    if (isrefreshing) {
      if (checkcurrentanimationpart(animationpart.first)) {
        offsety -= getanimationpartvalue(animationpart.first);
      } else if (checkcurrentanimationpart(animationpart.second)) {
        offsety -= getanimationpartvalue(animationpart.second);
      } else if (checkcurrentanimationpart(animationpart.third)) {
        offsety += getanimationpartvalue(animationpart.third);
      } else if (checkcurrentanimationpart(animationpart.fourth)) {
        offsety += getanimationpartvalue(animationpart.fourth);
      }
    }
 
    matrix.settranslate(offsetx, offsety);
 
    if (dragpercent == 1.0f) {
      matrix.prerotate(rotateangle, mjetwidthcenter, mjetheightcenter);
    }
 
    canvas.drawbitmap(mjet, matrix, null);
  }

动画效果已经画好了,下面我们来看看怎么结合下拉刷新来调用吧?
二、我们还需要自定义一个pulltorefreshview(下拉刷新)
1.我们的pulltorefreshview这里需要继承viewgroup
我们先把刚才定义的刷新时的动画加进来

?
1
2
3
4
5
6
7
private refreshview mrefreshview;
<pre name="code" class="java">private imageview mrefreshimageview;
<pre name="code" class="java">mrefreshimageview = new imageview(context);
    mrefreshview = new refreshview(getcontext(), this);
    mrefreshimageview.setimagedrawable(mrefreshview);
 
    addview(mrefreshimageview);

2.然后我们设置ontouchevent()事件

?
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
@override
  public boolean ontouchevent(@nonnull motionevent ev) {
 
    if (!misbeingdragged) {
      return super.ontouchevent(ev);
    }
 
    final int action = motioneventcompat.getactionmasked(ev);
 
    switch (action) {
      case motionevent.action_move: {
        final int pointerindex = motioneventcompat.findpointerindex(ev, mactivepointerid);
        if (pointerindex < 0) {
          return false;
        }
 
        final float y = motioneventcompat.gety(ev, pointerindex);
        final float ydiff = y - minitialmotiony;
        final float scrolltop = ydiff * drag_rate;
        mcurrentdragpercent = scrolltop / mtotaldragdistance;
        if (mcurrentdragpercent < 0) {
          return false;
        }
        float boundeddragpercent = math.min(1f, math.abs(mcurrentdragpercent));
        float extraos = math.abs(scrolltop) - mtotaldragdistance;
        float slingshotdist = mtotaldragdistance;
        float tensionslingshotpercent = math.max(0,
            math.min(extraos, slingshotdist * 2) / slingshotdist);
        float tensionpercent = (float) ((tensionslingshotpercent / 4) - math.pow(
            (tensionslingshotpercent / 4), 2)) * 2f;
        float extramove = (slingshotdist) * tensionpercent / 2;
        int targety = (int) ((slingshotdist * boundeddragpercent) + extramove);
 
        mrefreshview.setpercent(mcurrentdragpercent);
        settargetoffsettop(targety - mcurrentoffsettop, true);
        break;
      }
      case motioneventcompat.action_pointer_down:
        final int index = motioneventcompat.getactionindex(ev);
        mactivepointerid = motioneventcompat.getpointerid(ev, index);
        break;
      case motioneventcompat.action_pointer_up:
        onsecondarypointerup(ev);
        break;
      case motionevent.action_up:
      case motionevent.action_cancel: {
        if (mactivepointerid == invalid_pointer) {
          return false;
        }
        final int pointerindex = motioneventcompat.findpointerindex(ev, mactivepointerid);
        final float y = motioneventcompat.gety(ev, pointerindex);
        final float overscrolltop = (y - minitialmotiony) * drag_rate;
        misbeingdragged = false;
        if (overscrolltop > mtotaldragdistance) {
          setrefreshing(true, true);
        } else {
          mrefreshing = false;
          animateoffsettoposition(manimatetostartposition);
        }
        mactivepointerid = invalid_pointer;
        return false;
      }
    }
 
    return true;
  }

三、最后我们看怎样在activity中使用这个下拉刷新控件
1.先看一下布局文件
这里是我们的下拉刷新空间嵌套着我们的listview,然后我们再给listview填充数据即可

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".pulltorefreshactivity">
 
  <com.hankkin.animationpulltorefreshdemo.pulltorefreshview
    android:id="@+id/pull_to_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <listview
      android:id="@+id/list_view"
      android:divider="@null"
      android:dividerheight="0dp"
      android:fadingedge="none"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />
 
  </com.hankkin.animationpulltorefreshdemo.pulltorefreshview>
 
</relativelayout>

2.为listview填充数据
为了我们的效果比较好看,这里我们给listview的每一个item填充不同的颜色,看起来会比较高大上。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
map<string, integer> map;
    list<map<string, integer>> samplelist = new arraylist<map<string, integer>>();
 
 
    int[] colors = {
        r.color.saffron,
        r.color.eggplant,
        r.color.sienna};
 
    int[] tripnames = {
        r.string.trip_to_india,
        r.string.trip_to_italy,
        r.string.trip_to_indonesia};
 
    for (int i = 0; i < tripnames.length; i++) {
      map = new hashmap<string, integer>();
      map.put(sampleadapter.key_name, tripnames[i]);
      map.put(sampleadapter.key_color, colors[i]);
      samplelist.add(map);
    }
 
    listview listview = (listview) findviewbyid(r.id.list_view);
    listview.setadapter(new sampleadapter(this, r.layout.list_item, samplelist));

3.最后,我们再设置一下下拉刷新的监听事件就ok了

?
1
2
3
4
5
6
7
8
9
10
11
12
mpulltorefreshview = (pulltorefreshview) findviewbyid(r.id.pull_to_refresh);
    mpulltorefreshview.setonrefreshlistener(new pulltorefreshview.onrefreshlistener() {
      @override
      public void onrefresh() {
        mpulltorefreshview.postdelayed(new runnable() {
          @override
          public void run() {
            mpulltorefreshview.setrefreshing(false);
          }
        }, refresh_delay);
      }
    });

怎么样?有没有很高大上啊?

大家可以动手实践一下,希望对大家的学习有所帮助。