欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-02-13 10:52:04
最近看到了好多高端、大气、上档次的动画效果,如果给你的项目中加上这些动画,相信你的app一定很优秀,今天给大家分析一下来自yalantis的一个超好看的下拉刷新动画。 首...

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

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

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

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

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

private void initiatedimens() { 
    mscreenwidth = mcontext.getresources().getdisplaymetrics().widthpixels; 
    mjettopoffset = mparent.gettotaldragdistance() * 0.5f; 
    mtop = -mparent.gettotaldragdistance(); 
  } 

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

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.然后我们来画这个头

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); 
  } 
/** 
   * 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
我们先把刚才定义的刷新时的动画加进来

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()事件

@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填充数据即可

<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填充不同的颜色,看起来会比较高大上。

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了

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); 
      } 
    }); 

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

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