如何给SwipeRefreshLayout同时实现下拉刷新和上拉加载

来源:互联网 发布:观山4字能值多少钱知乎 编辑:程序博客网 时间:2024/06/07 02:21

尊重原创,转载请注明:http://blog.csdn.net/zj695469296/article/details/50563841

关于下拉刷新github上有很多的框架可以学习和借鉴,desmond111大神这样总结:

Repo性能拓展性综合建议Android-PullToRefresh★★★★★★★★由于作者不再维护,无法在gradle中配置,顶部视图难以拓展,不建议放入工程中使用android-Ultra-Pull-To-Refresh★★★★★★★★★★如之前分析,PtrClassicFrameLayout性能有缺陷;建议使用PtrFrameLayout,性能较好。这套库自定义能力很强,建议使用。android-pulltorefresh★★实现方式上有缺陷,拓展性也很差。优点就是代码非常简单,只能作为反面例子。Phoenix★★★★★★效果非常好,性能不错,可惜比较难拓展顶部视图,为了适配布局padding造成了性能损失,有优化空间。可以作为学习与练手的对象。FlyRefresh★★★★★★效果很新颖,可惜的是顶部视图计算动效上开销太大,优化空间较少,可以作为学习与练手的对象。SwipeRefreshLayout★★★★★官方出品,更新有保障,但是如上分析,其实性能上还是有点缺陷的,拓展性比较差,不建议放入工程中使用。综合的看,现在是utltra-pull-to-refresh的天下,这套我自己也用过,十分的好用而且拓展性极高。

他包含两部分:一是header,一是content,header可以自定义成任何的头部,content就是你要显示的内容了

他抽象出来了两个重要的接口:PtrHandler和PtrUIHandler,一般来说header实现PtrUIHandler接口,接口中可以为header实现在下拉开始,下拉进行中,下拉结束等状态进行相应的变化,而PtrHandler则是针对用户想要刷新数据或判断什么时候可以下拉的回调。

除此之外还有一个PtrFrameLayout这个类,他代表了一个自定义的viewgroup,有且只能有两个子view,就是header和content,跟所有的自定义控件一样会重写那几个方法,我们不需要去管他。下面贴例子:

布局文件:

<in.srain.cube.views.ptr.PtrFrameLayout        android:id="@+id/rotate_header_list_view_frame"        xmlns:cube_ptr="http://schemas.android.com/apk/res-auto"        android:layout_width="match_parent"        android:layout_height="match_parent"        cube_ptr:ptr_duration_to_close="500"        cube_ptr:ptr_duration_to_close_header="100"        cube_ptr:ptr_keep_header_when_refresh="true"        cube_ptr:ptr_pull_to_fresh="false"        cube_ptr:ptr_ratio_of_header_height_to_refresh="1.2"        cube_ptr:ptr_resistance="1.7">            <ListView                android:id="@+id/rotate_header_list_view"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:divider="@null"                android:listSelector="@android:color/transparent"                android:paddingLeft="12dp"                android:paddingRight="12dp"                />    </in.srain.cube.views.ptr.PtrFrameLayout>
代码:

final int[] i = {0};        ListView listView = (ListView) findViewById(R.id.rotate_header_list_view);        final BaseAdapter baseAdapter = new BaseAdapter() {            @Override            public int getCount() {                return 3;            }            @Override            public Object getItem(int position) {                return null;            }            @Override            public long getItemId(int position) {                return 0;            }            @Override            public View getView(int position, View convertView, ViewGroup parent) {                TextView textView = new TextView(MainActivity.this);                textView.setText(""+ i[0]++);                return textView;            }        };        listView.setAdapter(baseAdapter);        final PtrFrameLayout mPtrFrame = (PtrFrameLayout) findViewById(R.id.rotate_header_list_view_frame);        // header        final MaterialHeader header = new MaterialHeader(this);        int[] colors = getResources().getIntArray(R.array.google_colors);        header.setColorSchemeColors(colors);        //MATCH_PARENT = -1;        //WRAP_CONTENT = -2;        header.setLayoutParams(new PtrFrameLayout.LayoutParams(-1, -2));        LocalDisplay.init(MainActivity.this);        header.setPadding(0, LocalDisplay.dp2px(15), 0, LocalDisplay.dp2px(10));        header.setPtrFrameLayout(mPtrFrame);        mPtrFrame.setLoadingMinTime(2000);//设置最小的加载时间        mPtrFrame.setHeaderView(header);        mPtrFrame.addPtrUIHandler(header);        mPtrFrame.setPinContent(true);//不让content移动        mPtrFrame.setPtrHandler(new PtrHandler() {            @Override            public void onRefreshBegin(PtrFrameLayout frame) {                mPtrFrame.refreshComplete();                baseAdapter.notifyDataSetChanged();            }            @Override            public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {                return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header);            }        });
可以看见非常简单,utltra-pull-to-refresh的开发者liaohuqiu现在也在持续的更新,上面这里用的就是最新的material design风格的下拉刷新header,但是有个小bug,就是在header第一圈转完后会有一次明显的圆圈闪一下,然后再继续旋转,我改了两处小地方,现在能做到不闪而过,但觉得不够好,最后会有源码下载

====================================================

我这里是主推MD的下拉刷新方式的,既然上面的MD情况不够好,那么自然就找完美的方法了,还好谷歌新的v4包里退出了SwipeRefreshLayout,非常简单:

布局文件:

<android.support.v4.widget.SwipeRefreshLayout    android:id="@+id/swipe_container"    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <ScrollView        android:layout_width="match_parent"        android:layout_height="wrap_content">        <TextView            android:id="@+id/textView1"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:gravity="center"            android:paddingTop="10dp"            android:text="swipe_to_refresh"            android:textSize="20sp"            android:textStyle="bold"/>    </ScrollView></android.support.v4.widget.SwipeRefreshLayout>
代码:

final TextView tv = (TextView) findViewById(R.id.textView1);        final SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);        //设置刷新时动画的颜色,可以设置4个        swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light);        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {            @Override            public void onRefresh() {                tv.setText("正在刷新");                // TODO Auto-generated method stub                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        // TODO Auto-generated method stub                        tv.setText("刷新完成");                        swipeRefreshLayout.setRefreshing(false);                    }                }, 3000);            }        });
但是注意的是在content为webview的时候,下拉会跟webview的滚动发生冲突,解决的办法也非常简单,只需要重写ontouchevent即可:

import android.content.Context;import android.support.v4.widget.SwipeRefreshLayout;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.ViewGroup;/** * 为了不让webview的滚动事件和下拉更新的事件冲突,重写onTouchEvent事件,当webview滚动到最顶端的时候才有下拉刷新 */public class ScrollSwipeRefreshLayout extends SwipeRefreshLayout {    private ViewGroup viewGroup ;    public ScrollSwipeRefreshLayout(Context context) {        super(context);    }    public ScrollSwipeRefreshLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    public ViewGroup getViewGroup() {        return viewGroup;    }    public void setViewGroup(ViewGroup viewGroup) {        this.viewGroup = viewGroup;    }    @Override    public boolean onTouchEvent(MotionEvent arg0) {        if(null!=viewGroup){            if(viewGroup.getScrollY()> 1){                //直接截断时间传播,交给webview                return false;            }else{                //说明滑到了最上面,这时候就自己处理,出现下拉刷新                return super.onTouchEvent(arg0);            }        }        return super.onTouchEvent(arg0);    }}
代码:

webView = (WebView) this.findViewById(R.id.webview);        refreshLayout = (ScrollSwipeRefreshLayout) this.findViewById(R.id.refresh_layout);        refreshLayout.setViewGroup(webView);//设置监听滚动的子view,多了这一步        refreshLayout.setOnRefreshListener(this);        //设置颜色        refreshLayout.setColorScheme(R.color.green, R.color.gray, R.color.blue_50, R.color.light_white);        webView.loadUrl("http://blog.csdn.net/zj695469296/article/details/50563841");        webView.setWebChromeClient(new WebChromeClient() {            @Override            public void onProgressChanged(WebView view, int newProgress) {                super.onProgressChanged(view, newProgress);                if (newProgress == 100) {                    //设置加载完成后结束动画                    refreshLayout.setRefreshing(false);                }            }        });        webView.setWebViewClient(new WebViewClient() {            @Override            public boolean shouldOverrideUrlLoading(WebView view, String url) {                view.loadUrl(url);                return true;            }        });
下面再多个功能,在MD风格的下拉加载完成后,再来上拉加载:

import android.content.Context;import android.support.v4.widget.SwipeRefreshLayout;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.widget.AbsListView;import android.widget.ListView;/** * 继承自SwipeRefreshLayout,从而实现滑动到底部时上拉加载更多的功能. * 目前仅实现listview的加载更多 */public class RefreshLayout extends SwipeRefreshLayout implements AbsListView.OnScrollListener {    /**     * 滑动到最下面时的上拉操作     */    private int mTouchSlop;    /**     * listview实例     */    private ListView mListView;    /**     * 上拉监听器, 到了最底部的上拉加载操作     */    private OnLoadListener mOnLoadListener;    /**     * ListView的加载中footer     */    private View mListViewFooter;    /**     * 按下时的y坐标     */    private int mYDown;    /**     * 抬起时的y坐标, 与mYDown一起用于滑动到底部时判断是上拉还是下拉     */    private int mLastY;    /**     * 是否在加载中 ( 上拉加载更多 )     */    private boolean isLoading = false;    /**     * @param context     */    public RefreshLayout(Context context) {        this(context, null);    }    public RefreshLayout(Context context, AttributeSet attrs) {        super(context, attrs);        //getScaledTouchSlop表示一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如viewpager就是用这个距离来判断用户是否翻页        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();        //拿到footer        mListViewFooter = LayoutInflater.from(context).inflate(R.layout.listview_footer, null,                false);    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        // 初始化ListView对象        if (mListView == null) {            getListView();        }    }    /**     * 获取ListView对象     */    private void getListView() {        int childs = getChildCount();        if (childs > 0) {            //第一个是android.support.v4.widget.CircleImageView,SwipeRefreshLayout的下拉刷新的圆圈            //第二个才是ListView            View childView = getChildAt(1);            if (childView instanceof ListView) {                mListView = (ListView) childView;                // 设置滚动监听器给ListView, 使得滚动的情况下也可以自动加载                mListView.setOnScrollListener(this);            }        }    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        final int action = event.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN:                // 按下                mYDown = (int) event.getRawY();                break;            case MotionEvent.ACTION_MOVE:                // 移动                mLastY = (int) event.getRawY();                break;            case MotionEvent.ACTION_UP:                // 抬起                if (canLoad()) {                    loadData();                }                break;            default:                break;        }        return super.dispatchTouchEvent(event);    }    /**     * 是否可以加载更多, 条件是到了最底部, listview不在加载中, 且为上拉操作.     */    private boolean canLoad() {        return isBottom() && !isLoading && isPullUp();    }    /**     * 判断是否到了最底部,这里只是判断listview的     */    private boolean isBottom() {        if (mListView != null && mListView.getAdapter() != null) {            return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);        }        return false;    }    /**     * 是否是上拉操作,从触摸事件中判断     */    private boolean isPullUp() {        return (mYDown - mLastY) >= mTouchSlop;    }    /**     * 如果到了最底部,而且是上拉操作.那么执行onLoad方法     */    private void loadData() {        if (mOnLoadListener != null) {//只有用户设置了监听才有效果            // 设置状态为正在加载,以防重新加载            setLoading(true);            mOnLoadListener.onLoad();//回调        }    }    /**     * control footer to show or not     * @param loading     */    public void setLoading(boolean loading) {        isLoading = loading;        if (isLoading) {            mListView.addFooterView(mListViewFooter);        } else {            mListView.removeFooterView(mListViewFooter);            mYDown = 0;            mLastY = 0;        }    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,                         int totalItemCount) {        // 滚动时到了最底部也可以加载更多        if (canLoad()) {            loadData();        }    }    /**     * 加载更多的监听器     */    public interface OnLoadListener {        void onLoad();    }    public void setOnLoadListener(OnLoadListener loadListener) {        mOnLoadListener = loadListener;    }}
布局文件:

<?xml version="1.0" encoding="utf-8"?><demo.ultra_pull_to_refresh_demo.RefreshLayout    android:id="@+id/swipe_layout"    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <ListView        android:id="@+id/listview"        android:layout_width="match_parent"        android:layout_height="match_parent">    </ListView></demo.ultra_pull_to_refresh_demo.RefreshLayout>
代码:

// 模拟20条数据        final List<String> datas = new ArrayList<String>();        for (int i = 0; i < 20; i++) {            datas.add("item - " + i);        }        final BaseAdapter adapter = new ArrayAdapter<String>(this,                android.R.layout.simple_list_item_1,                datas);        ListView listView = (ListView) findViewById(R.id.listview);        listView.setAdapter(adapter);        // 获取RefreshLayout实例        final RefreshLayout myRefreshListView = (RefreshLayout) findViewById(R.id.swipe_layout);        // 设置下拉刷新时的颜色值,颜色值需要定义在xml中        myRefreshListView.setColorSchemeResources(R.color.color1,R.color.color2,R.color.color3,R.color.color4);        // 设置下拉刷新监听器        myRefreshListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {            @Override            public void onRefresh() {                Toast.makeText(MainActivity.this, "refresh", Toast.LENGTH_SHORT).show();                myRefreshListView.postDelayed(new Runnable() {                    @Override                    public void run() {                        // 更新数据                        datas.add(new Date().toGMTString());                        adapter.notifyDataSetChanged();                        // 更新完后调用该方法结束刷新                        myRefreshListView.setRefreshing(false);                    }                }, 3000);            }        });        // 加载监听器        myRefreshListView.setOnLoadListener(new RefreshLayout.OnLoadListener() {            @Override            public void onLoad() {                Toast.makeText(MainActivity.this, "load", Toast.LENGTH_SHORT).show();                myRefreshListView.postDelayed(new Runnable() {                    @Override                    public void run() {                        datas.add(new Date().toGMTString());                        adapter.notifyDataSetChanged();                        //让footer消失                        myRefreshListView.setLoading(false);                    }                }, 3000);            }        });
贴图:

源码下载


0 0
原创粉丝点击