安卓RefreshLayout实现上拉刷新下拉加载以及数据去重的列表页

在项目中app的build.gradle添加以下依赖:

//RefreshLayout
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.0'

//glide用于加载item中的图片
implementation 'com.github.bumptech.glide:glide:4.7.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'

首先来绘制一个简单的列表页布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.scwang.smartrefresh.layout.SmartRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/refreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:overScrollMode="never">
        </android.support.v7.widget.RecyclerView>
    </com.scwang.smartrefresh.layout.SmartRefreshLayout>

</LinearLayout>

然后绘制一下RecycleView中需要加载的item布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="150dp" >

    <RelativeLayout
        android:id="@+id/notice"
        android:layout_width="match_parent"
        android:layout_height="140dp"
        android:background="#FFFFFF">

        <TextView
            android:id="@+id/title"
            android:layout_width="250dp"
            android:layout_height="wrap_content"
            android:gravity="top"
            android:layout_marginTop="10dp"
            android:layout_marginLeft="10dp"
            android:text="发布一条很重要的公告"
            android:textColor="#000000"
            android:textSize="20dp"/>

        <TextView
            android:id="@+id/description"
            android:layout_width="250dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:layout_marginLeft="10dp"
            android:text="今天宿舍发布一条很重要的公告啊,我是公告的简介呢!今天宿舍发布一条很重要的公告啊,我是公告的简介呢!"
            android:layout_below="@+id/title"
            android:textSize="14dp"/>

        <ImageView
            android:id="@+id/img"
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:layout_alignParentRight="true"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp"
            android:scaleType="fitCenter"
            >
        </ImageView>

        <TextView
            android:id="@+id/time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginLeft="10dp"
            android:text="2020/02/02 15:30"
            android:textSize="16dp"/>

    </RelativeLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="10dp">
    </View>

</LinearLayout>

以下是item效果图:
在这里插入图片描述

在绘制一个提示没有数据的空页面布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >
        <ImageView
            android:layout_width="200dp"
            android:layout_height="220dp"
            android:layout_gravity="center"
            android:layout_marginTop="200dp"
            android:background="@drawable/cry"
            >
        </ImageView>
        <TextView
            android:id="@+id/layout_tishi"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:textColor="#000000"
            android:textSize="26dp"
            android:text="没有找到任何数据"
            android:gravity="center"
            >
        </TextView>
    </LinearLayout>
</android.support.constraint.ConstraintLayout>

在这里插入图片描述

然后整理一下业务逻辑流程(这里示例中数据进行本地缓存,想了解的请看另一篇关于Banner的博文):

①打开APP后请求服务器获取相关数据,若有数据则渲染item并通过适配器加载到RecycleView中,若无数据则加载空页面布局。②下拉刷新时请求服务器数据并重置RecycleView并重新渲染加载item。③上拉加载使用分页的逻辑加载下一页数据并更新在RecycleView中,此步需要对数据进行去重,以免由于加载时有新数据导致原本下一页的数据与之前数据重复。

接下来直接贴出完整代码,重要部分在注释中说明,这里示例使用的是Fragment,请根据自己项目适当修改,这里只演示流程和基本逻辑。

public class zixun extends Fragment {

    static RecyclerView recycleview;
    static int haveData = 0;	//当前列表数据条数
    static int total = 0;		//上次刷新时获取数据总条数
    static List<Map<String,String>> mDatas = new ArrayList<Map<String, String>>();	//当前数据列表
    static Map<String,String> noticeId = new HashMap<>();	//用于存储数据ID进行数据去重
    private HomeAdapter mAdapter;
    static int page = 1;	//当前数据页
    static int limit = 10;	//每页多少条
    static RefreshLayout refreshLayout;

    @Override
    public void onCreate(Bundle onSaveInstanceState){
        super.onCreate(onSaveInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
        View main_tab_zixun = inflater.inflate(R.layout.main_zixun,container,false);
        recycleview = main_tab_zixun.findViewById(R.id.recyclerView);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
        recycleview.setLayoutManager(layoutManager);
        layoutManager.setOrientation(OrientationHelper. VERTICAL);
        recycleview.setAdapter(mAdapter = new HomeAdapter());
        recycleview.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.HORIZONTAL));
        recycleview.setItemAnimator(new DefaultItemAnimator());
        refreshLayout = (RefreshLayout)main_tab_zixun.findViewById(R.id.refreshLayout);
        //设置刷新加载的动画效果
        refreshLayout.setRefreshHeader(new PhoenixHeader(getContext()));
        refreshLayout.setRefreshFooter(new BallPulseFooter(getContext()));
        refreshLayout.setDisableContentWhenRefresh(true);
        initData();	//先加载本地缓存的数据,之后在onresumen中刷新

        refreshLayout.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(RefreshLayout refreshlayout) {
            	//刷新时重置所有数据并重新设置适配器
                refreshlayout.autoRefresh();
                page = 1;
                mDatas.clear();
                noticeId.clear();
                refreshData();
            }
        });

        refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore(RefreshLayout refreshlayout) {
            	//是否加载只看当前数据是否小于第一次刷新时获取的总条数
                haveData = mDatas.size();
                refreshlayout.autoLoadMore();
                if (haveData<total){
                    loadData();
                }else{
                    Toasty.info(getActivity(),"真的没有数据了,刷新试试吧",Toast.LENGTH_SHORT).show();
                    refreshlayout.finishLoadMore(1000);
                }
            }
        });
        return main_tab_zixun;
    }

    @Override
    public void onResume() {
        super.onResume();
        refreshLayout.autoRefresh();	//刷新数据
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    protected void initData()
    {
    	//加载本地缓存并解析,这里不做阐述,可以看另一篇关于Banner的博文
        final SharedPreferences sp = getActivity().getSharedPreferences("zixun", Context.MODE_PRIVATE);
        String zixun_json_list = sp.getString("jsonList",null);
        if(zixun_json_list==null){

        }else{
            try {
                JSONArray dataArray = new JSONArray(zixun_json_list);
                for(int i=0;i<dataArray.length();i++){
                    JSONObject thisNotice = dataArray.getJSONObject(i);
                    Map<String,String> map = new HashMap<>();
                    map.put("title",thisNotice.getString("title"));
                    map.put("img_url",thisNotice.getString("img_url"));
                    map.put("description",thisNotice.getString("description"));
                    map.put("time",timeStamp2Date(thisNotice.getInt("create_time")+"000","yyyy-MM-dd HH:mm"));
                    mDatas.add(map);
                    noticeId.put("noticeId:"+thisNotice.getInt("id"),thisNotice.getInt("id")+"");
                }
                recycleview.setAdapter(mAdapter = new HomeAdapter());
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

    protected void refreshData()
    {
        String host = new Defines().SERVER_HOST+"getNotice";
        OkHttpUtils
                .post()
                .url(host)
                .addParams("page",page+"")
                .addParams("limit",limit+"")
                .build()
                .execute(new StringCallback() {

                    @Override
                    public void onError(Call call, Exception e, int id) {
                        refreshLayout.finishRefresh(1000);
                        Toasty.error(getActivity(), "服务器请求失败", Toast.LENGTH_SHORT, true).show();
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        try {
                            JSONObject jsonObject = new JSONObject(response);
                            int code = jsonObject.getInt("code");
                            if (code==200) {
                                total = jsonObject.getInt("total");
                                page = jsonObject.getInt("current_page");
                                JSONArray dataArray = jsonObject.getJSONArray("data");
                                final SharedPreferences sp = getActivity().getSharedPreferences("zixun", Context.MODE_PRIVATE);
                                sp.edit().putString("jsonList",dataArray.toString()).commit();
                                for(int i=0;i<dataArray.length();i++){
                                    JSONObject thisNotice = dataArray.getJSONObject(i);
                                    Map<String,String> map = new HashMap<>();
                                    map.put("title",thisNotice.getString("title"));
                                    map.put("img_url",thisNotice.getString("img_url"));
                                    map.put("description",thisNotice.getString("description"));
									//服务器给的是时间戳,这里进行转换                                    
                                    map.put("time",timeStamp2Date(thisNotice.getInt("create_time")+"000","yyyy-MM-dd HH:mm"));
                                    mDatas.add(map);
                                   	//将每条数据的ID存入map在加载时可以数据去重判断
									noticeId.put("noticeId:"+thisNotice.getInt("id"),thisNotice.getInt("id")+"");
                                }
                                //重新设置适配器
                                recycleview.setAdapter(mAdapter = new HomeAdapter());
                                refreshLayout.finishRefresh(1000);
                            } else {
                                refreshLayout.finishRefresh(1000);
                                Toasty.error(getActivity(),jsonObject.getString("msg"), Toast.LENGTH_SHORT, true).show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                            refreshLayout.finishRefresh(1000);
                            Toasty.error(getActivity(),"刷新失败",Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }

    protected void loadData()
    {
        String host = new Defines().SERVER_HOST+"getNotice";
        OkHttpUtils
                .post()
                .url(host)
                .addParams("page",(page+1)+"")
                .addParams("limit",limit+"")
                .build()
                .execute(new StringCallback() {

                    @Override
                    public void onError(Call call, Exception e, int id) {
                        refreshLayout.finishLoadMore(1000);
                        Toasty.error(getActivity(), "服务器请求失败", Toast.LENGTH_SHORT, true).show();
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        try {
                            JSONObject jsonObject = new JSONObject(response);
                            int code = jsonObject.getInt("code");
                            if (code==200) {
                                page = jsonObject.getInt("current_page");
                                JSONArray dataArray = jsonObject.getJSONArray("data");
                                for(int i=0;i<dataArray.length();i++){
                                    JSONObject thisNotice = dataArray.getJSONObject(i);
                                    Map<String,String> map = new HashMap<>();
                                    map.put("title",thisNotice.getString("title"));
                                    map.put("description",thisNotice.getString("description"));
                                    map.put("img_url",thisNotice.getString("img_url"));
                                    map.put("time",timeStamp2Date(thisNotice.getInt("create_time")+"000","yyyy-MM-dd HH:mm"));
                                    //若数据ID已存在则不加载
                                    if(!noticeId.containsValue(thisNotice.getInt("id")+"")){
                                        noticeId.put("noticeId:"+thisNotice.getInt("id"),thisNotice.getInt("id")+"");
                                        mDatas.add(map);
                                    }
                                }
                                mAdapter.notifyDataSetChanged();
                                refreshLayout.finishLoadMore(1000);
                            } else {
                                refreshLayout.finishLoadMore(1000);
                                Toasty.error(getActivity(),jsonObject.getString("msg"), Toast.LENGTH_SHORT, true).show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                            refreshLayout.finishLoadMore(1000);
                            Toasty.error(getActivity(),"加载失败",Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }

	//时间戳转时间字符串
    public static String timeStamp2Date(String seconds,String format) {
        if(seconds == null || seconds.isEmpty() || seconds.equals("null")){
            return "";
        }
        if(format == null || format.isEmpty())
            format = "yyyy-MM-dd HH:mm:ss";
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(new Date(Long.valueOf(seconds)));
    }

    class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
    {

        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
        {
            MyViewHolder holder;
            //如果数据为空则加载空页面布局
            if(mDatas == null || mDatas.isEmpty()){
                holder = new MyViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.empty_data, parent, false));
            }else{
                holder = new MyViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.item_home, parent, false));
            }
            return holder;
        }

        @Override
        public void onBindViewHolder(MyViewHolder holder, final int position)
        {
        	//若有数据则将数据匹配到item,若没有则设置空页面布局的提示
            if(!(mDatas == null || mDatas.isEmpty())){
                holder.time.setText(mDatas.get(position).get("time"));
                holder.titleTextView.setText(mDatas.get(position).get("title"));
                holder.descriptionTextView.setText(mDatas.get(position).get("description"));
                RequestOptions options = RequestOptions.centerInsideTransform().placeholder(R.drawable.loading).error(R.drawable.loading);
                Glide.with(getContext()).load(mDatas.get(position).get("img_url")).apply(options).into(holder.img);

                holder.relativeLayout.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toasty.info(getActivity(),"点了第"+(position+1)+"个Item,标题是:"+mDatas.get(position).get("title"),Toast.LENGTH_SHORT).show();
                    }
                });
            }else{
                holder.layout_tishi.setText("没有找到任何数据");
            }
        }

        @Override
        public int getItemCount()
        {
        	//这里一定要进行一个三目运算,如果没有数据也要返回1以加载空页面布局,否则RecycleView将不加载任何东西
            return mDatas.size()==0?1:mDatas.size();
        }

        class MyViewHolder extends RecyclerView.ViewHolder
        {
            TextView titleTextView;
            TextView descriptionTextView;
            TextView time;
            TextView layout_tishi;
            ImageView img;
            RelativeLayout relativeLayout;

            public MyViewHolder(View view)
            {
                super(view);
                //定义控件,这里标题和描述的textview进行了限制,超过部分用...显示
                if(mDatas == null || mDatas.isEmpty()){
                    layout_tishi = (TextView) view.findViewById(R.id.layout_tishi);
                }else {
                    img = (ImageView) view.findViewById(R.id.img);
                    titleTextView = (TextView) view.findViewById(R.id.title);
                    descriptionTextView = (TextView) view.findViewById(R.id.description);
                    time = (TextView) view.findViewById(R.id.time);
                    relativeLayout = (RelativeLayout) view.findViewById(R.id.notice);
                    titleTextView.setSingleLine(true);
                    titleTextView.setEllipsize(TextUtils.TruncateAt.END);
                    descriptionTextView.setMaxLines(3);
                    descriptionTextView.setEllipsize(TextUtils.TruncateAt.END);
                }
            }
        }
    }
}

接下来通过动态图看一下实际项目中的实现效果:

在这里插入图片描述

发布了29 篇原创文章 · 获赞 1 · 访问量 7807

猜你喜欢

转载自blog.csdn.net/qq_38280150/article/details/104227166
今日推荐