Android RefreshLayout implements pull-up refresh drop-down loading and data De duplication list pages

Keywords: Android xml encoding github

Add the following dependencies to build.gradle of app in the project:

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

//glide is used to load pictures in item
implementation 'com.github.bumptech.glide:glide:4.7.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'

First, draw a simple list page layout:

<?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>

Then draw the item layout to be loaded in RecycleView:

<?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="Make an important announcement"
            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="Today, the dormitory released a very important announcement. I am the introduction of the announcement!Today, the dormitory released a very important announcement. I am the introduction of the announcement!"
            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>

The following is the item rendering:

When drawing an empty page layout with no data:

<?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="No data found"
            android:gravity="center"
            >
        </TextView>
    </LinearLayout>
</android.support.constraint.ConstraintLayout>

Then organize the business logic process (in this example, data is cached locally, please see another blog about Banner for more information):

① After opening the APP, request the server to obtain relevant data. If there is any data, render the item and load it into RecycleView through the adapter. If there is no data, load the empty page layout. ② Drop down refresh requests server data and resets RecycleView and re renders loaded items. ③ Pull up loading uses the paging logic to load the next page data and update it in RecycleView. In this step, you need to de duplicate the data, so as not to duplicate the original data on the next page with the previous data due to new data during loading.

Next, paste the complete code directly. The important part is explained in the note. Here, the example uses Fragment. Please modify it according to your own project. Here, only the process and basic logic are demonstrated.

public class zixun extends Fragment {

    static RecyclerView recycleview;
    static int haveData = 0;	//Number of current list data
    static int total = 0;		//Get the total number of data in the last refresh
    static List<Map<String,String>> mDatas = new ArrayList<Map<String, String>>();	//Current data list
    static Map<String,String> noticeId = new HashMap<>();	//Used to store data ID for data De duplication
    private HomeAdapter mAdapter;
    static int page = 1;	//Current data page
    static int limit = 10;	//How many pieces per page
    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);
        //Set refresh loaded animation
        refreshLayout.setRefreshHeader(new PhoenixHeader(getContext()));
        refreshLayout.setRefreshFooter(new BallPulseFooter(getContext()));
        refreshLayout.setDisableContentWhenRefresh(true);
        initData();	//Load the locally cached data first, and then refresh it in onresumen

        refreshLayout.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(RefreshLayout refreshlayout) {
            	//Reset all data and reset adapter on refresh
                refreshlayout.autoRefresh();
                page = 1;
                mDatas.clear();
                noticeId.clear();
                refreshData();
            }
        });

        refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore(RefreshLayout refreshlayout) {
            	//Whether to load only depends on whether the current data is less than the total number of pieces obtained during the first refresh
                haveData = mDatas.size();
                refreshlayout.autoLoadMore();
                if (haveData<total){
                    loadData();
                }else{
                    Toasty.info(getActivity(),"There's really no data,Try refreshing",Toast.LENGTH_SHORT).show();
                    refreshlayout.finishLoadMore(1000);
                }
            }
        });
        return main_tab_zixun;
    }

    @Override
    public void onResume() {
        super.onResume();
        refreshLayout.autoRefresh();	//refresh data
    }

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

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

    protected void initData()
    {
    	//Load the local cache and parse it. I won't elaborate here. See another blog about 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(), "Server request failed", 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"));
									//The server gives a timestamp, which is converted here                                    
                                    map.put("time",timeStamp2Date(thisNotice.getInt("create_time")+"000","yyyy-MM-dd HH:mm"));
                                    mDatas.add(map);
                                   	//Save the ID of each data into the map, and the data can be de judged during loading
									noticeId.put("noticeId:"+thisNotice.getInt("id"),thisNotice.getInt("id")+"");
                                }
                                //Reset adapter
                                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(),"refresh failed",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(), "Server request failed", 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"));
                                    //Do not load if data ID already exists
                                    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(),"Failed to load",Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }

	//Timestamp to time string
    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;
            //Load empty page layout if data is empty
            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)
        {
        	//If there is data, match the data to the item; if not, set the prompt of empty page layout
            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(),"Order No."+(position+1)+"individual Item,The title is:"+mDatas.get(position).get("title"),Toast.LENGTH_SHORT).show();
                    }
                });
            }else{
                holder.layout_tishi.setText("No data found");
            }
        }

        @Override
        public int getItemCount()
        {
        	//There must be a binocular operation here. If there is no data, return 1 to load the empty page layout. Otherwise, RecycleView will not load anything
            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);
                //Define the control, where the title and description of textview are limited, and the excess part is displayed with
                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);
                }
            }
        }
    }
}

Next, we can see the implementation effect in the actual project through the dynamic diagram:

Published 29 original articles, won praise 1, visited 7757
Private letter follow

Posted by notaloser on Sat, 08 Feb 2020 06:16:24 -0800