上拉刷新和下拉刷新的实现

先来两张效果图

         

关于下拉刷新,Google提供了一个布局SwipRefreshLayout,它里面可以包涵一个滑动控件,然后你可以设置它的刷新事件就OK了,非常简单用。但是上拉刷新就有点麻烦了。网上很多方法都是给recyclerview添加footer的方法,我也采用这种方法实现了一个。其实也就是recyclerview的item多布局。一般布局和footer布局。,在上滑到最后一个item的时候,就把加一个footer类型的item,这里,然后很明显这里也就行滑动监听,就监听它上滑动到最后一个item。下面先。下面直接给代码

。导入依赖。由于wish我是用了用OKhttp就是实现的联网刷新,所以导入了OKhttp的依赖包,当然还得加联网权限。

 compile 'com.android.support:recyclerview-v7:25.3.1'
    compile 'com.squareup.okhttp:okhttp:2.4.0'
贴个footer的布局,因为footer包含两个部分“正在加载:”,和没有“没有更多了,”。用用的时候根据情况选择隐藏、显示或者gone
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">


    <LinearLayout
        android:id="@+id/line1"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:gravity="center">

        <ProgressBar
            android:layout_width="40dp"
            android:layout_height="40dp"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:gravity="center_vertical"
            android:layout_marginLeft="5dp"
            android:text="正在加载..."
            android:textSize="20dp"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/line2"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:gravity="center">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="没有更多了!"
            android:textSize="20sp"/>

    </LinearLayout>

</LinearLayout>

最核心的两个类adapter和监听。
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    private ArrayList<String> list;
    private final int ITEM=0;//加载的为一般item
    private final int FOOT=1;//加载footer

    //上拉加载的状态
    private int loadstate=2;//默认加载完成
    public final int LOADING = 1;//正在加载
    public final int FINISH = 2;// 加载完成
    public final int END = 3;  //没有更多数据了(显示另一footer)

    public MyAdapter(ArrayList<String> list) {
        this.list = list;
    }

    public void setLoadstate(int loadstate) {//用于动态设置加载状态
        this.loadstate = loadstate;
        notifyDataSetChanged();
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //通过加载类型,来显示不同的view。viewType由getItemViewType确定
        if (viewType == ITEM) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item, parent, false);
            return new ItemViewHodler(view);

        } else if (viewType == FOOT) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.foot, parent, false);
            return new FootViewHodler(view);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        //绑定viewholder的时候根据类型来绑定
        if (holder instanceof ItemViewHodler) {
            ItemViewHodler itemhodler= (ItemViewHodler) holder;
            itemhodler.textView.setText(list.get(position));
        } else if (holder instanceof FootViewHodler) {
            FootViewHodler foothoder = (FootViewHodler) holder;
            switch (loadstate){
                case LOADING:
                    foothoder.line1.setVisibility(View.VISIBLE);
                    foothoder.line2.setVisibility(View.GONE);
                    break;
                case FINISH:
                    //设置成INVISIBLE而不是gone,为了给footer预留位置,美观
                    foothoder.line1.setVisibility(View.INVISIBLE);
                    foothoder.line2.setVisibility(View.GONE);
                    break;
                case END:
                    foothoder.line1.setVisibility(View.GONE);
                    foothoder.line2.setVisibility(View.VISIBLE);
                    break;
            }
        }

    }

    @Override
    public int getItemViewType(int position) {
        //最后一个加载footer,
        if (position + 1 == getItemCount()) {
            return FOOT;
        } else {
            return ITEM;
        }
    }

    @Override
    public int getItemCount() {
        return list.size()+1;//因为要加上一个footer
    }

    

    private class ItemViewHodler extends RecyclerView.ViewHolder{

        TextView textView;
        public ItemViewHodler(View itemView) {
            super(itemView);
            textView= (TextView) itemView.findViewById(R.id.text);
        }
    }

    private class FootViewHodler extends RecyclerView.ViewHolder{

        LinearLayout line1;
        LinearLayout line2;
        public FootViewHodler(View itemView) {
            super(itemView);
            line1= (LinearLayout) itemView.findViewById(R.id.line1);
            line2= (LinearLayout) itemView.findViewById(R.id.line2);
        }
    }
}

public abstract class FootScrollListener extends RecyclerView.OnScrollListener {
    //通过重写RecyclerView的滑动监听来判断是否滑动到底部
    private boolean isScrollUp=false;

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        /*newState表示当前滑动的状态
        SCROLL_STATE_IDLE:不滑动
        SCROLL_STATE_DRAGGING;滑动(手指在屏幕上)
        SCROLL_STATE_SETTLING;滑动(手指移开屏幕)
        */
        LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
        // 当不滑动时
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            //返回最后一个完成可见视图位置
            int lastItemPosition = manager.findLastCompletelyVisibleItemPosition();
            int itemCount = manager.getItemCount();
            // 判断是否滑动到了最后一个item,并且是向上滑动
            if (lastItemPosition == (itemCount - 1) && isScrollUp) {
                // 加载更多
                myLoad();
            }
        }
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        /*
        这个方法实时监测页面(不是手指)滑动距离
        dx表示横向滑动距离
        dy表示纵向
        大于0向上,小于0向下,等于0不滑动
        */
        isScrollUp= dy>=0;

    }
    //这里可以用接口回调,然后就不用将此类设置成抽象类,
    //设置一个回调,
    public abstract void myLoad();
}

最后主活动类
public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private SwipeRefreshLayout refreshLayout;
    private MyAdapter myAdapter;
    private ArrayList<String> list=new ArrayList<String>();
    private static boolean isloading=false;//判断是否在加载

    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==1){
                refreshLayout.setEnabled(true);
                if (refreshLayout != null && refreshLayout.isRefreshing()) {
                            refreshLayout.setRefreshing(false);
                }
                myAdapter.setLoadstate(myAdapter.FINISH);
                isloading=false;
                smooth();
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initvew();
        somelisten();
    }

    public void initvew(){
        refreshLayout= (SwipeRefreshLayout) findViewById(R.id.refresh);
        //关于SwipeRefreshLayout的其他属性和方法,自行查阅文档

        recyclerView= (RecyclerView) findViewById(R.id.recycle);
        for(int i=0;i<25;i++) list.add("item");
        myAdapter=new MyAdapter(list);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        //recyclerView.setLayoutManager(new GridLayoutManager(this,3));
        recyclerView.setAdapter(myAdapter);
    }

    public void somelisten(){
        refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                //避免下拉刷新与上拉刷新冲突(虽然可能性很小),进行屏蔽,保证只有一个刷新
                if(!isloading){
                    isloading=true;
                    list.add(0,"newitem");
                    httppost();
                }
            }
        });

        recyclerView.addOnScrollListener(new FootScrollListener() {
            @Override
            public void myLoad() {
                /*
                SwipeRefreshLayout刷新加载的时候屏蔽了下拉刷新事件,避免同时刷新多次
                但是我们自定义的上拉刷新不能自己屏蔽,我们这里用静态变量来实现屏蔽功能
                */
                if(!isloading){
                    Log.e("刷新","进入");
                    myAdapter.setLoadstate(myAdapter.LOADING);
                    isloading=true;
                    refreshLayout.setEnabled(false);//上拉刷新的时候屏蔽掉下拉刷新
                    if (list.size() < 28) {
                        //网络请求
                        //list.add("lastitem");
                        httppost();


                    } else {
                        // 显示加载到底的提示
                        myAdapter.setLoadstate(myAdapter.END);
                    }
                }
            }
        });
    }

    //recyclerview滑动到适当位置
    public void smooth(){
        /*getChildAt返回组中指定位置的视图,组是指屏幕显示的item组
        *getTop返回此视图相对于其父项顶部的位置
        *smoothScrollBy沿任意轴给定的像素滑动
        *关于recyclerview滑动最常用的是smoothScrollToPosition,不做讲解
        * */
        int top=recyclerView.getChildAt(0).getTop();
        Log.e("top", String.valueOf(top));
        recyclerView.smoothScrollBy(0,top);
        Log.e("count", String.valueOf(recyclerView.getChildCount()));
    }

    public void httppost(){
        final OkHttpClient client=new OkHttpClient();
        RequestBody formBody = new FormEncodingBuilder()
                .add("passwd","123")
                .add("number","123")
                .build();
        final Request request=new Request.Builder()
                .url("http://10.126.149.157:8080/Test/HelloServlet")
                .post(formBody)
                .build();
        new Thread(){
            @Override
            public void run() {
                Response response = null;
                try {
                    response = client.newCall(request).execute();
                    if (response.isSuccessful()) {
                        Log.e("结果", response.body().string());
                        handler.sendEmptyMessage(1);
                    }
                    else {
                        Log.e("结果", "请求出错");
                        handler.sendEmptyMessage(1);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.e("结果", "抛出异常");
                    handler.sendEmptyMessageDelayed(1,500);
                }
            }
        }.start();
    }
}

我这里是直接拿了以前的OKhttp的一个代码段,当然服务器没开,这里就有一点需要注意的了,有时候网络请求需要很长时间才结束,在实际开发中肯定是不行,然后你可以再加一个计时器在里面,当请求时间过长时,直接停止刷新提示网络超时。


还有一个大问题,那就是当recyclerview是表格布局的时候,加的那个footer也是表格的一格,这样显得非常难看。所以要重写Adapter里这个函数onAttachedToRecyclerView 

根据文档解释,在recyclerview observe这个适配器的时候调用这个方法。通过这个方法判断网格布局的格数然后给footer设置占几个item。但是要注意,在你的主活动中必须将setadapter放在setlayout后面,因为这个方法里面做了网格布局的判断,设置了网格布局才有效。代码如下

 //setadapter必须放在setlayout的后面,不然不会调用到这个方法
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {

                    // 如果当前是footer的位置,那么该item占据2个单元格,正常情况下占据1个单元格
                    return getItemViewType(position) == FOOT ? gridManager.getSpanCount() : 1;
                }
            });
        }
    }

最后说几点应当注意的地方。上拉或者下拉正在刷新的时候应该不然它触发新的刷新,虽然这种情况很少,但是也要避免。网络请求的时候,时间是不可控的,有时候时间过长影响体验,所以应该在设一个计时器,刷新达到一定时间据强制关闭刷新并提醒(我的代码中没写这一段的代码)。当上拉的时候底部的footer应该要有不让它显示出来,这样有拖动效果,而这一部分区域正好用来被上拉刷新替换。然后刷新完后,footer还在,所以要上滑一段距离。最后再给一张改进过的效果图


代码已上传:http://download.csdn.net/download/lhp15575865420/10112127

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值