刷新控件SwipeRefreshLayout&bug解决

前言:

       下拉刷新在众多的App中可谓是屡见不鲜,在Version19.1之后Google将SwipeRefreshLayout添加到了support-v4包里面,是刷新控件,它只允许一个直接子类,操作上比较简单但也不免有一些坑,如滑动冲突SwipeRefreshLayout+ViewPager等等。


Part 1、SwipeRefreshLayout的基础应用

  xml如:

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="true">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="#FF0000">
            </FrameLayout>
        </LinearLayout>
    </android.support.v4.widget.SwipeRefreshLayout>

java中进行设置

        mRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srl);
        mRefreshLayout.setEnabled(true);
        mRefreshLayout.setDistanceToTriggerSync(300);
        mRefreshLayout.setProgressViewOffset(false, 200, 300);
        mRefreshLayout.setColorSchemeResources(R.color.swipe_color_1,R.color.swipe_color_2,R.color.swipe_color_3,R.color.swipe_color_4);
        mRefreshLayout.setSize(SwipeRefreshLayout.LARGE);
        mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mRefreshLayout.setRefreshing(false);
                    }
                }, 5000);
            }
        });
tips:

1、setDistanceToTriggerSync(int) // 设置手指在屏幕下拉多少距离会触发下拉刷新

2、setProgressViewOffset(boolean,int,int)//设置小圆圈的偏移量 1、是否进行缩放  2、开始出现的位置  3、最远出现的位置

3、setColorSchemeResources(int...color)//设置进度条的颜色主题  4种

4、setSize(int) //设置圆圈的大小,这里只提供了两种 DEFAULT和LARGE

5、setOnRefreshListener()//设置刷新的监听

效果~


当然你也可以在代码中将View添加在SwipeRefreshLayout里面,如:

        View view = super.onCreateView(inflater, container, savedInstanceState);
        mRefreshLayout = new MySwipeRefreshLayout(getContext());
        mRefreshLayout.addView(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mRefreshLayout.setLayoutParams(
                new ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT));
tips:

1、这里继承了ListFragment,生成View使用super.onCreateView()方法

2、addView方法添加ViewGroup.LayoutParams.MATCH_PARENT来确保填充fragment或者是SwipeRefreshLayout控件


Part 2、SwipeRefreshLayout进阶使用

               1、实现进入Activity便进行刷新功能

代码如下:

        mRefreshLayout.setRefreshing(true);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mRefreshLayout.setRefreshing(false);
            }
        },2000);
通过查看源码
    public void setRefreshing(boolean refreshing) {
        if (refreshing && mRefreshing != refreshing) {
            // scale and show
            mRefreshing = refreshing;
            ...
            mNotify = false;
            startScaleUpAnimation(mRefreshListener);
        } else {
            setRefreshing(refreshing, false /* notify */);
        }
    }
可以看到不管是if还是else都涉及到了mRefreshListener,查看mRefreshListener

    private Animation.AnimationListener mRefreshListener = new Animation.AnimationListener() {
        ...
        @Override
        public void onAnimationEnd(Animation animation) {
            if (mRefreshing) {
                // Make sure the progress view is fully visible
                mProgress.setAlpha(MAX_ALPHA);
                mProgress.start();
                if (mNotify) {
                    if (mListener != null) {
                        mListener.onRefresh();
                    }
                }
                mCurrentTargetOffsetTop = mCircleView.getTop();
            } else {
                reset();
            }
        }
    };
由于setRefreshing(boolean)方法将mNotify置为false,所以必然不会执行到mListener.onRefresh()方法。如果想通过手动设置刷新并且触发事件则需要调用

   private void setRefreshing(boolean refreshing, final boolean notify) {
因为是私有的方法则通过反射调用

                try {
                    Class clazz = mRefreshLayout.getClass();
                    Method method = clazz.getDeclaredMethod("setRefreshing",boolean.class,boolean.class);
                    method.setAccessible(true);
                    method.invoke(mRefreshLayout,true,true);
                }catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
               2、当存在多个View引起SwipeRefreshLayout的bug

<FrameLayout 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="com.andly.administrator.andly_md01.swiperefleshlayout.fragment.MultiViewFragment">
        <GridView
            android:id="@android:id/list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:numColumns="2" />
        <TextView
            android:id="@android:id/empty"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="空内容"
            android:layout_gravity="center"/>
</FrameLayout>
在布局文件中除了ListView或GridView之类的滑动控件外又新增了其它的控件,刚好这些控件是在滑动控件的上面则会出现如下效果

无论滑动到哪里,只要是向上滑动就会出现滑动控件

解决方案:

        @Override
        public boolean canChildScrollUp() {
        }
重写此方法,当滑动的时候会不停的调用此方法,只需要在这里面判断当前是否ListView或GridView的顶部即可

        @Override
        public boolean canChildScrollUp() {
            if (mSwipeableChildren != null && mSwipeableChildren.length > 0) {
                for (View view : mSwipeableChildren) {
                    if (view != null && view.isShown() && !canViewScrollUp(view)) {
                        // 如果这个View显示和不能向上滑动则返回false
                        return false;
                    }
                }
            }
            return true;
        }

        private boolean canViewScrollUp(View view) {
            if (android.os.Build.VERSION.SDK_INT >= 14) {
                // 4.0以上通过canScrolVertical方法来判断
                return ViewCompat.canScrollVertically(view, -1);
            } else {
                if (view instanceof AbsListView) {
                    // 当4.0以上则通过当前可见Item的位置和第一个Item的top值来判断
                    final AbsListView listView = (AbsListView) view;
                    return listView.getChildCount() > 0 &&
                            (listView.getFirstVisiblePosition() > 0
                                    || listView.getChildAt(0).getTop() < listView.getPaddingTop());
                } else {
                    // 对于其它非滑动类型控件通过ScrollY来判断
                    return view.getScrollY() > 0;
                }
            }
        }
tips:

1、SwipeRefreshLayout控件只有一个直接子控件

   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;
                }
            }
        }
    }
从源码可以看出将第一个子控件赋值给了mTarget
2、canChildScrollUp():ChildView是否可以向上滑动,如果可以则返回true(即不显示滑动进度)

Part 3、SwipeRefreshLayout冲突解决

ViewPager+SwipeRefreshLayout引起的滑动冲突,当你在SwipeRefreshLayout引入ViewPager时,会引起当你从左下右下进行滑动的时候出现刷新的效果而不是ViewPager翻页的效果

效果~

解决方案:

之所以出现如上的效果是因为触摸事件被SwipeRefreshLayout吸收了,所以ViewPager便不能处理该事件,我们只需要重新复写SwipeRefreshLayout

    public boolean onInterceptTouchEvent(MotionEvent ev) {
在这里面通过判断触摸偏移量来决定是否将事件交给ViewPager来处理

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                startX = ev.getX();
                startY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if(isVp){//如果当前是ViewPager滑动状态则直接交给ViewPager来进行处理
                    return false;
                }
                float endX = ev.getX();
                float endY = ev.getY();
                float dx = Math.abs(endX-startX);
                float dy = Math.abs(endY - startY);
                if(dx > touchSlop && dx > dy){
                    isVp = true;
                    return false;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                isVp = false;
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

ViewPager+SwipeRefreshLayout的更多详情请看:ViewPager+SwipeRefreshLayout冲突





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值