zoukankan      html  css  js  c++  java
  • 仿9GAG制作过程(四)

    有话要说:

    这次主要讲述主页面下拉刷新和上拉加载功能的实现。

    主要是使用了SwipeRefreshLayout的布局方式,并在此基础上通过RecyclerView的特性增加了上拉加载的功能。

    成果:

    实现方式:

    页面布局:

     1 <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:id="@+id/swipeRefreshView"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent">
     5     <android.support.v7.widget.RecyclerView
     6         android:id="@+id/recyclerView"
     7         android:background="#f0f0f0"
     8         android:layout_width="match_parent"
     9         android:layout_height="match_parent"/>
    10 </android.support.v4.widget.SwipeRefreshLayout>

    通过SwipeRefreshLayout来实现下拉刷新功能。

    下拉刷新:

     1       SwipeRefreshLayout swipeRefreshLayout;        
     2       // 下拉刷新控件
     3         swipeRefreshLayout = getView().findViewById(R.id.swipeRefreshView);
     4         // 设置下拉控件背景色
     5         swipeRefreshLayout.setProgressBackgroundColorSchemeColor(Color.WHITE);
     6         // 设置下来控件主色
     7         swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent);
     8         // 设置下拉刷新事件
     9         swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    10             @Override
    11             public void onRefresh() {
    12                 new Thread(new Runnable() {
    13                     @Override
    14                     public void run() {
    15                         try {
    16                             Thread.sleep(1000);
    17                         } catch (InterruptedException e) {
    18                             e.printStackTrace();
    19                         }
    20                         String url = baseUrl + (++currentPage);
    21                         OkHttpClient client = new OkHttpClient();
    22                         Request request = new Request.Builder()
    23                                 .url(url)
    24                                 .build();
    25                         try {
    26                             Response response = client.newCall(request).execute();
    27                             String json = response.body().string();
    28                             if (json != null) {
    29                                 Gson gson = new Gson();
    30                                 List<NewsBean> newDatas = gson.fromJson(json, new TypeToken<List<NewsBean>>(){}.getType());
    31                                 if (newsBeans != null && newsBeans.size() > 0) {
    32                                     newsBeans.addAll(0, newDatas);
    33                                 }
    34                             }
    35                             Message message = new Message();
    36                             message.what = UPDATE_NEWS;
    37                             handler.sendMessage(message);
    38                         } catch (IOException e) {
    39                             e.printStackTrace();
    40                         }
    41                     }
    42                 }).start();
    43             }
    44         });

    通过setProgressBackgroundColorSchemeColor来设置下拉控件的背景色,也就是圈圈的主体颜色。

    通过setColorSchemeResources来设置下拉控件中间的线条颜色。

    通过setOnRefreshListener来定义下拉刷新事件。

    然后通过currentPage来实现下拉刷新之后获取的数据是下一页的数据,再添加到集合开头。

     1 private Handler handler = new Handler() {
     2         @Override
     3         public void handleMessage(Message msg) {
     4             switch (msg.what) {
     5                 case QUERY_NEWS:
     6                     recyclerView.getAdapter().notifyDataSetChanged();
     7                     break;
     8                 case UPDATE_NEWS:
     9                     recyclerView.getAdapter().notifyDataSetChanged();
    10                     swipeRefreshLayout.setRefreshing(false);
    11                     break;
    12                 case LOAD_MORE:
    13                     ((NewsAdapter)recyclerView.getAdapter()).changeStatus(NewsAdapter.UNLOADING);
    14                     currentState = NewsAdapter.UNLOADING;
    15                     break;
    16             }
    17         }
    18     };

    刷新完成之后,通过notifyDataSetChanged告诉RecyclerView数据改变了,进而更改页面显示。通过setRefreshing来控制下拉刷新控件的显示。

    由此,完成了下拉刷新的实现。

    上拉加载:

    由于SwipeRefreshLayout并不提供上拉加载的功能,于是准备利用RecyclerView灵活的特性来实现上拉加载功能。

      1 package com.example.lanxingren.imitating9gag.adapter;
      2 
      3 import android.content.Context;
      4 import android.support.annotation.NonNull;
      5 import android.support.v7.widget.CardView;
      6 import android.support.v7.widget.RecyclerView;
      7 import android.view.LayoutInflater;
      8 import android.view.View;
      9 import android.view.ViewGroup;
     10 import android.widget.ImageView;
     11 import android.widget.TextView;
     12 
     13 import com.example.lanxingren.imitating9gag.R;
     14 import com.example.lanxingren.imitating9gag.bean.NewsBean;
     15 import com.example.lanxingren.imitating9gag.util.GlideApp;
     16 
     17 import java.util.List;
     18 
     19 public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
     20 
     21     private List<NewsBean> myNewsList;
     22     private Context myContext;
     23 
     24     // 是否加载
     25     public static final int LOADING = 1;
     26     public static final int UNLOADING = 2;
     27 
     28     private int mStatus = UNLOADING;// 当前加载状态
     29 
     30     // item的viewType
     31     private final int ITEM = 1;
     32     private final int FOOTER = 2;
     33 
     34     static class NewsHolder extends RecyclerView.ViewHolder {
     35         CardView cardView;
     36         TextView titleView;
     37         ImageView imageView;
     38         TextView pointView;
     39         ImageView likeImageView;
     40         ImageView unlikeImageView;
     41 
     42 
     43         private NewsHolder (View view) {
     44             super(view);
     45             cardView = (CardView) view;
     46             titleView = view.findViewById(R.id.item_title);
     47             imageView = view.findViewById(R.id.item_image);
     48             pointView = view.findViewById(R.id.item_point);
     49             likeImageView = view.findViewById(R.id.item_like);
     50             unlikeImageView = view.findViewById(R.id.item_unlike);
     51         }
     52     }
     53 
     54     static class FooterHolder extends RecyclerView.ViewHolder {
     55         CardView cardView;
     56         private FooterHolder (View view) {
     57             super(view);
     58             cardView = view.findViewById(R.id.cardView_footer);
     59         }
     60     }
     61 
     62     public NewsAdapter (List<NewsBean> newsList) {
     63         this.myNewsList = newsList;
     64     }
     65 
     66     @Override
     67     public int getItemCount() {
     68         int count = 0;
     69         if (myNewsList != null) {
     70             count = myNewsList.size() + 1;
     71         }
     72         return count;
     73     }
     74 
     75     @NonNull
     76     @Override
     77     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
     78         if (myContext == null) {
     79             myContext = parent.getContext();
     80         }
     81 
     82         if (viewType == ITEM) {
     83             View view = LayoutInflater.from(myContext).inflate(R.layout.item_news, parent, false);
     84             return new NewsHolder(view);
     85         } else {
     86             View view = LayoutInflater.from(myContext).inflate(R.layout.item_footer, parent, false);
     87             return new FooterHolder(view);
     88         }
     89     }
     90 
     91     @Override
     92     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
     93         if (holder instanceof NewsHolder) {
     94             final NewsHolder newsHolder = (NewsHolder)holder;
     95             final NewsBean newsBean = myNewsList.get(position);
     96 
     97             // 设置标题
     98             String title = "9GAG#" + newsBean.getId();
     99             if (newsBean.getTitle() != null && newsBean.getTitle().length() > 0) {
    100                 title = newsBean.getTitle();
    101             }
    102             newsHolder.titleView.setText(title);
    103 
    104             // 屏幕宽度
    105             int screenWidth = myContext.getResources()
    106                     .getDisplayMetrics()
    107                     .widthPixels;
    108             // 屏幕高度
    109             int screenHeight = myContext.getResources()
    110                     .getDisplayMetrics()
    111                     .heightPixels;
    112 
    113             // 设置图片,不知道为什么override这样设置就可以让图片正正好显示,有时间研究一下?
    114             if (newsBean.getUrls() != null && newsBean.getUrls().size() > 0) {
    115                 GlideApp.with(myContext)
    116                         .load(newsBean.getUrls().get(0))
    117                         .override(screenWidth, screenHeight)
    118                         .into(newsHolder.imageView);
    119             }
    120 
    121             // 设置点赞数
    122             String point = Integer.toString(newsBean.getLike() - newsBean.getUnlike());
    123             newsHolder.pointView.setText(point);
    124 
    125             // 设置点赞图标显示
    126             int accentColor = myContext.getResources().getColor(R.color.colorAccent);
    127             int defaultColor = myContext.getResources().getColor(R.color.defaultColor);
    128             switch (newsBean.getIsLiked()) {
    129                 case -1:
    130                     newsHolder.likeImageView.setColorFilter(defaultColor);
    131                     newsHolder.unlikeImageView.setColorFilter(accentColor);
    132                     break;
    133                 case 0:
    134                     newsHolder.likeImageView.setColorFilter(defaultColor);
    135                     newsHolder.unlikeImageView.setColorFilter(defaultColor);
    136                     break;
    137                 case 1:
    138                     newsHolder.likeImageView.setColorFilter(accentColor);
    139                     newsHolder.unlikeImageView.setColorFilter(defaultColor);
    140                     break;
    141             }
    142 
    143             // 初始化监听事件
    144             newsHolder.likeImageView.setOnClickListener(new View.OnClickListener() {
    145                 @Override
    146                 public void onClick(View v) {
    147                     newsBean.setIsLiked(1);
    148                     notifyDataSetChanged();
    149                 }
    150             });
    151             newsHolder.unlikeImageView.setOnClickListener(new View.OnClickListener() {
    152                 @Override
    153                 public void onClick(View v) {
    154                     newsBean.setIsLiked(-1);
    155                     notifyDataSetChanged();
    156                 }
    157             });
    158 
    159         } else if (holder instanceof FooterHolder) {
    160             switch (mStatus) {
    161                 case UNLOADING:
    162                     ((FooterHolder) holder).cardView.setVisibility(View.GONE);
    163                     break;
    164                 case LOADING:
    165                     ((FooterHolder) holder).cardView.setVisibility(View.VISIBLE);
    166                     break;
    167             }
    168         }
    169     }
    170 
    171     @Override
    172     public int getItemViewType(int position) {
    173         if (position + 1 == getItemCount()) {
    174             return FOOTER;
    175         } else {
    176             return ITEM;
    177         }
    178     }
    179 
    180     public void changeStatus(int status) {
    181         this.mStatus = status;
    182         notifyDataSetChanged();
    183     }
    184 }

    在适配器中定义了两个布局,一个是普通布局,一个是尾布局(footer)。

    下面是footer的具体布局:

     1 <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:layout_width="match_parent"
     3     android:layout_height="wrap_content"
     4     xmlns:app="http://schemas.android.com/apk/res-auto"
     5     android:layout_marginTop="10dp"
     6     android:layout_marginBottom="0dp"
     7     app:cardCornerRadius="0dp"
     8     android:elevation="0dp"
     9     android:id="@+id/cardView_footer">
    10     <ProgressBar
    11         android:layout_width="wrap_content"
    12         android:layout_height="100dp"
    13         android:layout_gravity="center_horizontal"
    14         />
    15 </android.support.v7.widget.CardView>

    注意点:

    1. 通过mStatus来判断尾布局是否展示
    2. ITEM代表的是普通布局,即段子的布局;FOOTER代表的是尾布局
    3. 由于增加了一个item,故getItemCount得在原先的基础上加上一
    4. 在onCreateViewHolder中通过viewType来创建不同的viewHolder
    5. 实现getItemViewType方法,根据position的值来确定viewType
    6. 在onBindViewHolder中先判断viewHolder的类型,如果是尾布局(footer)的话,再根据mStatus来判断是否展示
    7. changeStatus主要是给外面用的,通过该方法可以控制footer的显示

    使用上拉加载:

     1 private void initLoadMoreListener() {
     2         recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
     3             @Override
     4             public void onScrollStateChanged(final RecyclerView recyclerView, int newState) {
     5                 super.onScrollStateChanged(recyclerView, newState);
     6 
     7                 // 获取当前可见的item位置
     8                 int lastVisiblePosition = 0;
     9                 RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    10                 if (layoutManager instanceof LinearLayoutManager) {
    11                     lastVisiblePosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
    12                 }
    13 
    14                 // 当前加载状态是UNLOADING && 当前可见的item位置是最后一条时
    15                 if (currentState == NewsAdapter.UNLOADING
    16                         && lastVisiblePosition + 1 == recyclerView.getAdapter().getItemCount()) {
    17                     // 改变footer的可见性
    18                     ((NewsAdapter)recyclerView.getAdapter()).changeStatus(NewsAdapter.LOADING);
    19                     currentState = NewsAdapter.LOADING;
    20 
    21                     new Thread(new Runnable() {
    22                         @Override
    23                         public void run() {
    24                             try {
    25                                 Thread.sleep(1000);
    26                             } catch (InterruptedException e) {
    27                                 e.printStackTrace();
    28                             }
    29 
    30                             String url = baseUrl + (++currentPage);
    31                             OkHttpClient client = new OkHttpClient();
    32                             Request request = new Request.Builder()
    33                                     .url(url)
    34                                     .build();
    35                             try {
    36                                 Response response = client.newCall(request).execute();
    37                                 String json = response.body().string();
    38                                 if (json != null) {
    39                                     Gson gson = new Gson();
    40                                     List<NewsBean> newDatas = gson.fromJson(json, new TypeToken<List<NewsBean>>(){}.getType());
    41                                     if (newsBeans != null && newsBeans.size() > 0) {
    42                                         newsBeans.addAll(newsBeans.size(), newDatas);
    43                                     }
    44                                 }
    45                                 Message message = new Message();
    46                                 message.what = LOAD_MORE;
    47                                 handler.sendMessage(message);
    48                             } catch (IOException e) {
    49                                 e.printStackTrace();
    50                             }
    51                         }
    52                     }).start();
    53                 }
    54             }
    55         });
    56     }

    通过给recyclerView加上滚动事件来实现下拉加载功能,具体逻辑如下:

    获取页面可见最下面的item位置

    →判断当前尾布局的加载状态

    →如果尾布局现在的状态是UNLOADING && item位置为最后一个,则开线程加载数据

    →将尾布局展示,即开始转圈动画

    →请求网络,获取数据,放入数据集

    →根据handler的处理,告诉RecyclerView数据改变,然后将footer隐藏

    通过以上的过程就可以实现上拉加载的效果。

    参考:

    SwipeRefreshLayout详解和自定义上拉加载更多SwipeRefreshLayout + RecyclerView 实现 上拉刷新 和 下拉刷新

    结束语:

    本次学习了Android的下拉刷新以及上拉加载的实现,对RecyclerView有了进一步的了解。

    接下来准备实现点赞功能以及GIF的暂停功能。

    大家如果有什么疑问或者建议可以通过评论或者邮件的方式联系我,欢迎大家的评论~

  • 相关阅读:
    Navicat 创建mysql存过、定时执行存过
    windows 系统 MySQL_5.6.21安装教程
    ldf和mdf文件怎么还原到sqlserver数据库
    免安装的tomcat转服务
    关掉IE提示“当前安全设置会使计算机有风险”
    U盘制作系统启动盘方法
    Tomcat窗口标题,中文乱码解决方法
    MyEclipse10安装SVN插件
    IE浏览器的卸载操作
    739. Daily Temperatures 每日温度
  • 原文地址:https://www.cnblogs.com/lanxingren/p/9173259.html
Copyright © 2011-2022 走看看