欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

仿9GAG制作过程(四)

程序员文章站 2022-06-18 18:24:05
有话要说: 这次主要讲述主页面下拉刷新和上拉加载功能的实现。 主要是使用了SwipeRefreshLayout的布局方式,并在此基础上通过RecyclerView的特性增加了上拉加载的功能。 成果: 实现方式: 页面布局: 通过SwipeRefreshLayout来实现下拉刷新功能。 下拉刷新: 通 ......

有话要说:

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

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

成果:

仿9GAG制作过程(四)

实现方式:

页面布局:

 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隐藏

 

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

参考:

结束语:

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

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

 

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