简单的Post
请求,以及RecycelrView
添加FooterView
,上拉加载更多练习,本打算是练习post
请求,但写着写着,成了RecyclerView
的练习
1. 完整的Acitivity代码
添加FooterView
,思路是Android 优雅的为RecyclerView添加HeaderView和FooterView
public class PostActivity extends AppCompatActivity implements ResultCallback2> {
private Platform mPlatform;
private int page = 1;
private RecyclerAdapter adapter;
private SwipeRefreshLayout swipeRefreshLayout;
private boolean isLoading = false;
private List oldsList = new ArrayList<>();
private HeaderAndFooterAdapter footerAdapter;
private RecyclerView recyclerView;
private boolean isFirst = true;
private View footerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_post);
mPlatform = Platform.get();
initView();
}
/**
* 初始化recyclerView
*/
private void initView() {
//RecyclerView
recyclerView = (RecyclerView) findViewById(R.id.activity_post_rv);
GridLayoutManager layoutManager = new GridLayoutManager(PostActivity.this, 2);
layoutManager.setOrientation(GridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
adapter = new RecyclerAdapter(recyclerView, R.layout.rv_item_layout);
//使用HeaderAndFooterAdapter
footerAdapter = new HeaderAndFooterAdapter(adapter);
recyclerView.addItemDecoration(new RVItemDecoration(16));
//FooterView
footerView = this.getLayoutInflater().inflate(R.layout.footer_view_layout, recyclerView, false);
footerView.setVisibility(View.GONE);
footerAdapter.addFootView(footerView);
recyclerView.setAdapter(footerAdapter);
//SwipeRefreshLayout
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.activity_post_srl);
swipeRefreshLayout.setColorSchemeColors(Color.parseColor("#FF4081"));
//第一次进来有自动刷新的效果
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
}
});
swipeRefreshLayout.setEnabled(false);
//请求第一页
request(page);
//上拉加载更多
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (!recyclerView.canScrollVertically(RecyclerView.VERTICAL) && !isLoading && newState == RecyclerView.SCROLL_STATE_IDLE) {
footerView.setVisibility(View.VISIBLE);
request(++page);
}
}
});
//设置点击事件
adapter.setItemListener(new CommonBaseAdapter.onRecyclerItemClickerListener() {
@Override
public void onRecyclerItemClick(View view, Object data, int position) {
ResponseBean.ShowapiResBodyBean.NewslistBean bean = (ResponseBean.ShowapiResBodyBean.NewslistBean) data;
ToastUtils.show(PostActivity.this, bean.getTitle());
}
});
}
/**
* 网络请求
*/
private void request(int page) {
isLoading = true;
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.build();
RequestBody requestBody = new FormBody.Builder()
.add(Urls.KEY_APPID, Urls.APPID)
.add(Urls.KEY_SIGN, Urls.SIGN)
.add(Urls.KEY_NUM, Urls.NUM)
.add(Urls.KEY_PAGE, page + "")
.build();
Request request = new Request.Builder().url(Urls.POST_URL).post(requestBody).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendFailResultCallback(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
ResponseBody responseBody = null;
try {
if (call.isCanceled()) {
sendFailResultCallback(new IOException("Request Canceled"));
return;
}
if (response.isSuccessful()) {
responseBody = response.body();
String json = responseBody.string();
ResponseBean responseBean = new Gson().fromJson(json, ResponseBean.class);
DiffUtilCallback callback = new DiffUtilCallback();
callback.setOldLists(oldsList);
oldsList.addAll(responseBean.getShowapi_res_body().getNewslist());
callback.setNewLists(oldsList);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callback);//子线程,计算差异
//成功回调
sendSuccessResultCallback(diffResult, oldsList);
} else {
sendFailResultCallback(new IOException("Request Failed"));
}
} catch (Exception e) {
sendFailResultCallback(e);
} finally {
if (null != responseBody) {
responseBody.close();
}
}
}
});
}
@Override
public void sendFailResultCallback(final Exception e) {
doSomething(new Runnable() {
@Override
public void run() {
//对应在onCreate()中的创建方式
//关闭刷新小圆圈
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(false);
}
});
footerView.setVisibility(View.GONE);
String info = "Fail Message --> " + e.getMessage();
ToastUtils.show(PostActivity.this, info);
footerView.setVisibility(View.GONE);
}
});
}
@Override
public void sendSuccessResultCallback(final DiffUtil.DiffResult diffResult, final List listt) {
isLoading = false;
doSomething(new Runnable() {
@Override
public void run() {
diffResult.dispatchUpdatesTo(footerAdapter);//将DiffUtil的结果,关联到Adapter
//记得将新的数据,存进adapter的List中
adapter.setData(listt);
footerView.setVisibility(View.GONE);
if (isFirst) {
recyclerView.scrollToPosition(0);
isFirst = false;
}
if (swipeRefreshLayout.isRefreshing()) {
swipeRefreshLayout.setRefreshing(false);
}
}
});
}
private void doSomething(Runnable runnable) {
mPlatform.execute(runnable);
}
}
代码不多,关键地方有注释
在成功请求到结果后,使用了DiffUtil
代替adapter.notifyDataSetChanged()
上拉加载更多,用的recyclerView.canScrollVertically(RecyclerView.VERTICAL)
,返回结果代表是否可以向上垂直滑动,false
就意味着到了RecycelrView
的底部
当RecycelrView
到达底部时,就让原本处于View.GONE
的FooterView
,显示出来,当加载完成时,再View.GONE
使用HeaderAndFooterAdapter装饰者
这样的方式,就可以不考虑各种Position
问题,不需要担心添加了FooterView
后,点击事件会错乱,RecyclerView
的adapter
不会受啥影响,扩展也不会影响HeaderAndFooterAdapter
,耦合度比较高
1.1 布局代码
1.1.1 Item布局
RecyclerView
的item
直接使用了一个CardView
,内部是一个ImageView
1.1.2 FooterView的布局代码:
使用了CardView
来显示一个白色的圆
1.2 OkHttp Post请求
- 创建
okHttpClient
对象
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.build();
connectTimeout(long timeout,TimeUnit unit)
,设置连接超时的时间
cache(Cache cache)
,设置缓存
cookieJar(CookieJar jar)
,存储,使用Cookie
addInterceptor(Interceptor i)
,添加拦截器
- 创建
RequestBody
对象
RequestBody requestBody = new FormBody.Builder()
.add(Urls.KEY_APPID, Urls.APPID)
.add(Urls.KEY_SIGN, Urls.SIGN)
.add(Urls.KEY_NUM, Urls.NUM)
.add(Urls.KEY_PAGE, page + "")
.build();
RequestBody
是一个abstract
类,FormBody
是Requestbody
的子类。在建造者模式中,我个人理解FormBody
属于产品
,而FormBody
的内部类Builder
属于具体建造者
add(String name, String value)
,添加查询条件
- Request 和 Call
Request
,默认是GET
请求,通过post(RequestBody)
,进行POST
请求
Call
,GET
和POST
请求一样,都是enqueue(Callback)
在子线程进行
1.3 DiffUtilCall
使用DiffUtilCall
代替notifyDataSetChanged()
public class DiffUtilCallback extends DiffUtil.Callback {
private List newLists = new ArrayList<>();
private List oldLists = new ArrayList<>();
public void setNewLists(List newLists) {
if (null != newLists) {
this.newLists.clear();
this.newLists.addAll(newLists);
}
}
public void setOldLists(List oldLists) {
if (null != oldLists) {
this.oldLists.clear();
this.oldLists.addAll(oldLists);
}
}
@Override
public int getOldListSize() {
return oldLists.size();
}
@Override
public int getNewListSize() {
return newLists.size();
}
/**
*根据图片地址,比较NewsListBean是否相同
*/
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
ResponseBean.ShowapiResBodyBean.NewslistBean newBean = newLists.get(newItemPosition);
ResponseBean.ShowapiResBodyBean.NewslistBean oldBean = oldLists.get(oldItemPosition);
return oldBean.getPicUrl().equals(newBean.getPicUrl());
}
/**
*只有当 areItemsTheSame 方法返回 true 时,才会调用此方法
* 比较Title是否相同
*/
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
ResponseBean.ShowapiResBodyBean.NewslistBean newBean = newLists.get(newItemPosition);
ResponseBean.ShowapiResBodyBean.NewslistBean oldBean = oldLists.get(oldItemPosition);
return oldBean.getTitle().equals(newBean.getTitle());
}
/**
* 得到差异的对象,最终通过 Bundle ,存进了List
主要就是重写3
个方法
使用很方便,伪码:
//在 OkHttp 执行网络请求的子线程中
DiffUtilCallback callback = new DiffUtilCallback();
callback.setOldLists(oldsList);
callback.setNewLists(newsList);
//在子线程中,计算差异
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callback);
//回调,在UI线程中
diffResult.dispatchUpdatesTo(footerAdapter);//将DiffUtil的结果,关联到Adapter
//记得将新的数据,存进adapter的List中
adapter.setData(listt);
对应的,需要重写adapter
中的onBindViewHolder(BaseViewHolder holder, int position, List
1.4 Adapter
CommonBaseAdapter
是一个很简单的适配器封装
public class RecyclerAdapter extends CommonBaseAdapter {
public RecyclerAdapter(RecyclerView rv, @LayoutRes int itemLayoutId) {
super(rv, itemLayoutId);
}
@Override
public void bindViewData(BaseViewHolder holder, ResponseBean.ShowapiResBodyBean.NewslistBean item, int position) {
show(holder, item);
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position, List
在3个参数的onBindViewHolde()
方法中,先对payloads
进行判断isEmpty()
如果payloads
不为empty
,就取出数据进行加载
在show()
方法中,根据布局中ImageView
的大小进行加载
2. 最后
写的跑题了,哈哈
有错误请指出
共勉 :)