Android 微信刷新&自带下拉刷新SwipeRefreshLayout(二)

上一篇博客Android 微信刷新&自带下拉刷新SwipeRefreshLayout(一)我们简单介绍了一下SwipeRefreshLayout的原理,以及对它对了一个简单的拆分,这一节我们就来改改它的代码,实现一个微信朋友圈的下拉刷新效果:

这里写图片描述

我们先看一下系统自带的下拉刷新效果:

这里写图片描述

下拉刷新的时候,可以看到一个进度条view,这个进度条view上一篇博客有抽取出来的,不懂的小伙伴可以去看看哈。

下面说一下仿微信下拉刷新的思路:

1、修改进度条view为我们微信的图片,然后给个动画让图片旋转

2、修改微信图片出现的位置为左上角

3、当下拉的时候,header图片跟着下拉

好啦!明确了我们的目的之后,我们就开始修改代码了。

首先替换掉默认的加载进度条为微信的图片
我们看到MaterialProgressDrawable这个类,这个就是进度条view(上一节也有提到),我们仿照着自己写一个叫WechatProgressDrawable的,代码比较简单,我就直接上代码了:

package com.yasin.eledemo.swipe;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;

import com.yasin.eledemo.R;

import java.util.ArrayList;

/**
 * Created by leo on 17/5/14.
 */

public class WechatProgressDrawable extends Drawable implements Animatable{
    private Resources mResources;
    private View mParent;
    /** The list of animators operating on this drawable. */
    private final ArrayList<Animation> mAnimators = new ArrayList<Animation>();


    // Maps to ProgressBar.Large style
    static final int LARGE = 0;
    // Maps to ProgressBar default style
    static final int DEFAULT = 1;
    static final float LARGE_SIZE=56;
    static final float DEFAULT_SIZE=40;
    static final long ANIMATION_DURATION=3000;
    private float mWidth,mHeight;
    private Animation mAnimation;
    private float mRotation;
    private Bitmap mBitmap;
    public WechatProgressDrawable(Context context, View parent) {
        mParent = parent;
        mResources = context.getResources();
        updateSizes(DEFAULT);
        setupAnimators();
        mBitmap= BitmapFactory.decodeResource(mResources, R.mipmap.icon_wechat1);
        mBitmap=Bitmap.createScaledBitmap(mBitmap,(int)mWidth,(int)mHeight,true);
    }
    public void updateSizes( int size) {
        if (size == LARGE) {
            setSizeParameters(LARGE_SIZE, LARGE_SIZE);
        } else {
            setSizeParameters(DEFAULT_SIZE, DEFAULT_SIZE);
        }
    }

    private void setSizeParameters(float width, float height) {
        final DisplayMetrics metrics = mResources.getDisplayMetrics();
        final float screenDensity = metrics.density;

        mWidth = width * screenDensity;
        mHeight = height * screenDensity;
    }
    private void setupAnimators() {
        final Animation animation = new Animation() {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                float rotation=interpolatedTime*1.0f*360;
                setProgressRotation(rotation);
            }
        };
        animation.setRepeatCount(Animation.INFINITE);
        animation.setRepeatMode(Animation.RESTART);
        animation.setInterpolator(new LinearInterpolator());
        animation.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                // do nothing
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        mAnimation = animation;
    }
    @Override
    public void draw(Canvas c) {
        final Rect bounds = getBounds();
        final int saveCount = c.save();
        c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
        c.drawBitmap(mBitmap,0,0,null);
        c.restoreToCount(saveCount);
    }
    /**
     * Set the amount of rotation to apply to the progress spinner.
     *
     * @param rotation Rotation is from [0..1]
     */
    public void setProgressRotation(float rotation) {
        this.mRotation=rotation;
        invalidateSelf();
    }
    @Override
    public int getIntrinsicHeight() {
        return (int) mHeight;
    }

    @Override
    public int getIntrinsicWidth() {
        return (int) mWidth;
    }
    @Override
    public void setAlpha(int alpha) {
    }
    public int getAlpha() {
        return 255;
    }
    @Override
    public void setColorFilter(ColorFilter colorFilter) {
    }
    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public void start() {
        mAnimation.reset();
        mAnimation.setDuration(ANIMATION_DURATION);
        mParent.startAnimation(mAnimation);
    }

    @Override
    public void stop() {
        mParent.clearAnimation();
        setProgressRotation(0);
    }

    @Override
    public boolean isRunning() {
        final ArrayList<Animation> animators = mAnimators;
        final int N = animators.size();
        for (int i = 0; i < N; i++) {
            final Animation animator = animators.get(i);
            if (animator.hasStarted() && !animator.hasEnded()) {
                return true;
            }
        }
        return false;
    }
}

我们来测试一下:

 WechatProgressDrawable drawable1=new WechatProgressDrawable(this,container);
        drawable1.setAlpha(255);
        mShopCart.setImageDrawable(drawable1);
        drawable1.start();

这里写图片描述

卧槽,gif录制出问题了还是咋了,调用了start方法后中间那个图标是可以旋转的。

好啦!!搞定基础的进度条view后,我们需要把它放进SwipeRefreshLayout中,在SwipeRefreshLayout中我们看到原有的

  int mSpinnerOffsetEnd;

    MaterialProgressDrawable mProgress;

    private Animation mScaleAnimation;

我们把 MaterialProgressDrawable对象改为


    int mSpinnerOffsetEnd;

    WechatProgressDrawable mProgress;

    private Animation mScaleAnimation;

好啦!可能有些地方的api你得删掉了,比如说原有的:

mProgress.showArrow(true);

这些方法你就得删掉了,因为我们的WechatProgressDrawable里面没有这些方法,并且我们也不需要了。

接下来我们需要把WechatProgressDrawable摆放在左上角的位置,让它可以从左上角出来:

在SwipeRefreshLayout的onlayout方法中处理了,
原来是这样的:

 @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        final int width = getMeasuredWidth();
        final int height = getMeasuredHeight();
        if (getChildCount() == 0) {
            return;
        }
        if (mTarget == null) {
            ensureTarget();
        }
        if (mTarget == null) {
            return;
        }
        final View child = mTarget;
        final int childLeft = getPaddingLeft();
        final int childTop = getPaddingTop();
        final int childWidth = width - getPaddingLeft() - getPaddingRight();
        final int childHeight = height - getPaddingTop() - getPaddingBottom();
        child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        int circleWidth = mCircleView.getMeasuredWidth();
        int circleHeight = mCircleView.getMeasuredHeight();
        mCircleView.layout((width / 2 - circleWidth / 2), mCurrentTargetOffsetTop,
                (width / 2 + circleWidth / 2), mCurrentTargetOffsetTop + circleHeight);
    }

改过之后:

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        final int width = getMeasuredWidth();
        final int height = getMeasuredHeight();
        if (getChildCount() == 0) {
            return;
        }
        if (mTarget == null) {
            ensureTarget();
        }
        if (mTarget == null) {
            return;
        }
        final View child = mTarget;
        final int childLeft = getPaddingLeft();
        final int childTop = getPaddingTop();
        final int childWidth = width - getPaddingLeft() - getPaddingRight();
        final int childHeight = height - getPaddingTop() - getPaddingBottom();
        child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        int circleWidth = mCircleView.getMeasuredWidth();
        int circleHeight = mCircleView.getMeasuredHeight();
        mCircleView.layout((100), mCurrentTargetOffsetTop,
                (100+circleWidth), mCurrentTargetOffsetTop + circleHeight);
    }

我就简单的设置其在左边100px的位置了。

好吧~修改完毕后,我们就可以简单的玩起来了~~~我就不测试了哈!!

为了和微信几乎一样的效果,我们需要加一个header(也就是一个imageview),然后当往下拉的时候,图片也往下拉,手指松开后,图片回到原有的位置。

其实呢也很简单,

<?xml version="1.0" encoding="utf-8"?>
<com.yasin.eledemo.swipe.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/id_swipe"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >
        <ImageView
            android:id="@+id/id_head_view"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:src="@mipmap/icon_beaty"
            android:layout_marginTop="-40dp"
            android:scaleType="centerCrop"
            />

可以看到,我在LinearLayout下面直接放了一个imageview,然后设置其默认样式为 android:layout_marginTop=”-40dp。

然后我们通过下拉的距离改变android:layout_marginTop来实现我们的图片滑动操作。

首先我们需要在SwipeRefreshLayout中定义一个view叫:

 private View mHeadView;

然后我们需要找到我们的 private View mHeadView;并且记录一下原始的margintop值:

private float originHeaderMargin;
    private void ensureTarget() {
        // Don't bother getting the parent height if the parent hasn't been laid
        // out yet.
        if (mTarget == null) {
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                if (!child.equals(mCircleView)) {
                    mTarget = child;
                    break;
                }
            }
        }
        if(mTarget!=null){
            mHeadView=mTarget.findViewById(R.id.id_head_view);
            if(mHeadView!=null&&mHeadView.getLayoutParams() instanceof MarginLayoutParams){
                MarginLayoutParams lp= (MarginLayoutParams) mHeadView.getLayoutParams();
                if(originHeaderMargin==0){
                    originHeaderMargin=lp.topMargin;
                    headerBackAni.setDuration(ANIMATE_TO_START_DURATION);
                    headerBackAni.setInterpolator(new AccelerateDecelerateInterpolator());
                }
            }
        }
    }

ensureTarget这个方法我上一节有介绍哈~~

然后当手指滑动的时候,我们需要根据滑动的距离改变margintop值,所以我们进入到SwipeRefreshLayout的onTouchEvent方法中修改下逻辑:

  @Override
case MotionEvent.ACTION_MOVE: {
                pointerIndex = ev.findPointerIndex(mActivePointerId);
                if (pointerIndex < 0) {
                    Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
                    return false;
                }

                final float y = ev.getY(pointerIndex);
                startDragging(y);

                if (mIsBeingDragged) {
                    final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
                    if (overscrollTop > 0) {
                        Log.e("TAG", "onTouchEvent:--->"+overscrollTop);
                        moveSpinner(overscrollTop);
                        //开始控制headerview的滑动位置
                        setHeaderMarginOffset(overscrollTop/2);
                    } else {
                        return false;
                    }
                }
                break;
    }
 private void setHeaderMarginOffset(float offset) {
        if (mHeadView != null && mHeadView.getLayoutParams() instanceof MarginLayoutParams) {
            MarginLayoutParams lp = (MarginLayoutParams) mHeadView.getLayoutParams();
            lp.topMargin = (int) (originHeaderMargin+offset);
            mHeadView.setLayoutParams(lp);
        }
    }

然后手指放开的时候,动画回到原来的位置:

case MotionEvent.ACTION_UP: {
                pointerIndex = ev.findPointerIndex(mActivePointerId);
                if (pointerIndex < 0) {
                    Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id.");
                    return false;
                }

                if (mIsBeingDragged) {
                    final float y = ev.getY(pointerIndex);
                    final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
                    mIsBeingDragged = false;
                    finishSpinner(overscrollTop);
                    //header动画回到原来的位置
                    startHeaderBackAni();
                    mProgress.stop();
                }
                mActivePointerId = INVALID_POINTER;
                return false;
            }
    }
    private void startHeaderBackAni(){
        mHeadView.clearAnimation();
        mHeadView.startAnimation(headerBackAni);
    }
 private final Animation headerBackAni=new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (mHeadView != null && mHeadView.getLayoutParams() instanceof MarginLayoutParams) {
                MarginLayoutParams lp = (MarginLayoutParams) mHeadView.getLayoutParams();
                float startMargin=lp.topMargin;
                float endMargin= originHeaderMargin;
                float margin=startMargin+(endMargin-startMargin)*interpolatedTime;
                lp.topMargin= (int) margin;
                mHeadView.setLayoutParams(lp);
            }
        }
    };

好啦!!我们的改造就撸完了~~

附上项目的github链接:
https://github.com/913453448/EleDemo

欢迎入群~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值