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: