热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

超好看的下拉刷新动画Android代码实现,效果简单大方,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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

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

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

一、首先我们需要自定义刷新的动态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()  1) { 
          y = 0; 
          while (y == 0) { 
            float tmp = (float) (mParent.getTotalDragDistance() / (Math.random() * RANDOM_Y_COEFFICIENT)); 
 
            for (Map.Entry 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 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  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; 
private ImageView mRefreshImageView; 
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填充数据即可

 
 
   
 
     
 
   
 
 

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

Map map; 
    List> sampleList = new ArrayList>(); 
 
 
    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 (); 
      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); 
      } 
    }); 

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

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


推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 【Windows】实现微信双开或多开的方法及步骤详解
    本文介绍了在Windows系统下实现微信双开或多开的方法,通过安装微信电脑版、复制微信程序启动路径、修改文本文件为bat文件等步骤,实现同时登录两个或多个微信的效果。相比于使用虚拟机的方法,本方法更简单易行,适用于任何电脑,并且不会消耗过多系统资源。详细步骤和原理解释请参考本文内容。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
author-avatar
幸福的小馋豆
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有