摘要:本文将带你了解Android应用开发之仿9GAG制作过程,希望本文对大家学Android有所帮助。
这次主要讲述主页面下拉刷新和上拉加载功能的实现。
主要是使用了SwipeRefreshLayout的布局方式,并在此基础上通过RecyclerView的特性增加了上拉加载的功能。
成果:
实现方式:
页面布局:
1 <android.support.v4.widget.SwipeRefreshLayout xmlns:android="//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="//schemas.android.com/apk/res/android"
2 android:layout_width="match_parent"
3 android:layout_height="wrap_content"
4 xmlns:app="//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>
?
注意点:
通过mStatus来判断尾布局是否展示
ITEM代表的是普通布局,即段子的布局;FOOTER代表的是尾布局
由于增加了一个item,故getItemCount得在原先的基础上加上一
在onCreateViewHolder中通过viewType来创建不同的viewHolder
实现getItemViewType方法,根据position的值来确定viewType
在onBindViewHolder中先判断viewHolder的类型,如果是尾布局(footer)的话,再根据mStatus来判断是否展示
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的暂停功能。
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号