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

来源:互联网 发布:三菱plc模块化编程 编辑:程序博客网 时间:2024/06/07 09:52

上一篇博客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方法中修改下逻辑:

  @Overridecase 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

欢迎入群~~~~

阅读全文
0 0