SwipeRefreshLayout 仿IOS ,下拉弹回效果

来源:互联网 发布:知乎er是什么意思 编辑:程序博客网 时间:2024/05/18 02:52

以前的项目下拉刷新用的都是XListView,用久了之后就有些审美疲劳了。谷歌推出的SwipeRefreshLayout很好用,样式也蛮好看的,用起来也比较方便。但是强迫症表示,还是希望能有下拉弹出和弹回去的效果,就尝试下SwipeRefreshLayout增加下拉弹出效果。

效果图

这里写图片描述


大致思路:获取SwipeRefreshLayout子控件的位置,在SwipeRefreshLayout的dispatchTouchEvent方法里处理滑动监听事件,计算滑动的距离,移动子控件的位置,达到弹出的效果。

直接上代码:

package com.example.zet.uncaughtexception;import android.content.Context;import android.graphics.Rect;import android.support.v4.widget.SwipeRefreshLayout;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.animation.TranslateAnimation;/** * Created by 仓颉 on 2015/11/16. */public class MySwiperefreshlayout extends SwipeRefreshLayout {    private static final String TAG = "MySwiperefreshlayout ";    //比如手指移动了100px, 那么View就只移动50px    //目的是达到一个延迟的效果    private static final float MOVE_MODE = 0.5f;    //弹回去的时间    private static final int ANIM_TIME = 300;    //MySwiperefreshlayout的子View,这个表示RecycleView    private View view;    //MySwiperefreshlayout的子View,这个表示CircleImageView(就是那个圈圈)    private View child;    //手指按下时的Y值, 用于在移动时计算移动距离    private float startY;    //用于记录正常的布局位置    private Rect rect = new Rect();    //在手指滑动的过程中记录是否移动了布局    private boolean isMoved = false;    //是否已经移动到顶部,手指按下时,判断是否能够下拉    private boolean isTop = false;    //延迟时间    public static int time;    public static int getTime() {        return time;    }    public static void setTime(int time) {        MySwiperefreshlayout.time = time;    }    public MySwiperefreshlayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        if (getChildCount() > 0) {            child = getChildAt(0);            view = getChildAt(getChildCount() - 1);        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        if (view == null) return;        //RecycleView的初始信息,这个位置保留之后,保持不变        rect.set(view.getLeft(), view.getTop(), view                .getRight(), view.getBottom());    }    /**     * 滑动监听,处理下拉事件     */    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        if (view == null) {            return super.dispatchTouchEvent(ev);        }        int action = ev.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN:                canDropDown();                //记录按下时的Y值                startY = ev.getY();                break;            case MotionEvent.ACTION_UP:                if (!isMoved) break;  //没有移动布局,不执行                //移动的距离                int distance = (int) ((int) (ev.getY() - startY) * MOVE_MODE);                //如果移动的距离大于CircleImageView.getTop,那么缩小移动距离,达到加载中的效果                if (distance > child.getTop()) {                    distance = child.getTop() + 30;                }                //Recycle移动到CircleImageView下拉的距离,表示加载中                view.layout(rect.left, rect.top + distance,                        rect.right, rect.bottom + distance);                // 开启动画                TranslateAnimation anim = new TranslateAnimation(0, 0, view.getTop(),                        rect.top);                anim.setDuration(ANIM_TIME);                if (time > 0) {                    //设置延迟时间                    //移动的最大距离 小于这个距离不刷新                    if (distance > 92) {                        anim.setStartOffset(time);                    }                }                view.startAnimation(anim);                // 设置回到正常的布局位置                view.layout(rect.left, rect.top,                        rect.right, rect.bottom);                //重置                isTop = false;                isMoved = false;                startY = 0;                break;            case MotionEvent.ACTION_MOVE:                //是否允许移动                if (!canDropDown()) {                    break;                }                //计算手指移动的距离                int distanceMove = (int) (ev.getY() - startY);                //允许移动并且是上拉的情况次下,移动布局                if (isTop && distanceMove > 0) {                    int offset = (int) (distanceMove * MOVE_MODE);                    //随着手指的移动而移动布局                    view.layout(rect.left, rect.top + offset,                            rect.right, rect.bottom + offset);                    isMoved = true;  //记录移动了布局                }                break;            default:                break;        }        return super.dispatchTouchEvent(ev);    }    /**     * 判断是否滚动到顶部     */    private boolean canDropDown() {        if (child.getVisibility() == View.VISIBLE)            isTop = true;        else            isTop = false;        return isTop;    }}

实现原理很简单,唯一不好判断的子控件是否滑动到顶部了。因为在Swiperefreshlayout的 getScrollY()一直是为0的,这里用了一个取巧的办法,就是判断CircleImageView是否显示,查看源码我们可以看到,CircleImageView是Swiperefreshlayout初始化的时候加载进去的,当滑动到顶部的时候,CircleImageView状态就为VISIBLE了。

这里写图片描述

这里写图片描述

0 0
原创粉丝点击