首页 > 系统 > Android > 正文

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

2020-04-11 10:54:07
字体:
来源:转载
供稿:网友

最近看到了好多高端、大气、上档次的动画效果,如果给你的项目中加上这些动画,相信你的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() < 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);       }     }); 

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

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表