刷新控件SwipeRefreshLayout&bug解决

来源:互联网 发布:数控火焰切割手动编程 编辑:程序博客网 时间:2024/06/08 04:10

前言:

       下拉刷新在众多的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冲突





1 0
原创粉丝点击