简介
在安卓开发中,我们会经常遇到上拉加载和下拉刷新的功能,通过网络请求拿到数据然后添加到控件上,之前自己也在网上搜索过一些文章,但基本上用处不大,要么是效果不一样,要么是贴的代码不完整,从而导致功能无法正常使用(很蛋疼),所以在摸索了一阵之后,完成了一个大致OK的demo,供大家参考。有问题还望大家指正出来,感激不尽。。
老规矩,先贴效果图
使用到得控件和工具简介:
- RefreshLayout(上拉加载下拉刷新控件)
- Adapter(加载数据的适配器)
- RecyclerView(加载数据的view控件)
- Handler(网络通信)
好了,开始上代码吧!!!
既然用到了网络,就要放入网络权限
<uses-permission android:name="android.permission.INTERNET"/>
布局XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/cyan">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="@dimen/dimen_60_dip"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="@string/jokes"
android:textColor="@color/black"
android:textSize="@dimen/dimen_22_dip" />
<ImageView
android:id="@+id/joke_img_back"
android:layout_width="@dimen/dimen_30_dip"
android:layout_height="@dimen/dimen_30_dip"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginStart="@dimen/dimen_10_dip"
app:srcCompat="@mipmap/back" />
</RelativeLayout>
<com.scwang.smartrefresh.layout.SmartRefreshLayout
android:id="@+id/activity_joke_refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:srlEnablePreviewInEditMode="false">
<com.scwang.smartrefresh.layout.header.ClassicsHeader
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srlAccentColor="@color/black"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/joke_img_load"
android:src="@mipmap/load"
android:layout_marginTop="@dimen/dimen_20_dip"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_70_dip" />
<android.support.v7.widget.RecyclerView
android:id="@+id/joke_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_80_dip"
android:layout_marginTop="@dimen/dimen_8_dip"
android:src="@mipmap/bottom_bg"/>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_0.5_dip"
android:layout_marginTop="@dimen/dimen_5_dip"
android:background="@color/darkgrey"/>
</LinearLayout>
<com.scwang.smartrefresh.layout.footer.ClassicsFooter
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srlAccentColor="@color/black"/>
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
</LinearLayout>
注:这个SmartRefreshLayout是自定义的一个框架,源自GitHub,gradle里面添加一下就可以啦
MainActivity.class
/**
* activity
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
/** activity */
Activity activity = this;
/** 请求的key */
public static String Joke_APPKEY = "e9bbc8a5de090451bd5da96dc574a94a";
/** 请求随机获取笑话的URL地址 */
public static final String HTTPURLS = "http://v.juhe.cn/joke/randJoke.php?";
/** 执行动画对象 */
private static Animation rotateAnimation;
/** 网络请求返回码 */
static final int SUCC_CODE = 0;
/** 返回按钮和加载中按钮 */
ImageView joke_img_back, joke_img_load;
/** 加载内容的RV */
RecyclerView joke_rv;
/** 添加数据的适配器 */
JokeLVAdapter adapter;
/** 自定义刷新和加载的标识,默认为false */
boolean isRef, isLoad = false;
/** swf:这个是上拉刷新和加载框架 */
RefreshLayout activity_joke_refreshLayout;
/** 使用handler请求网络数据并在handleMessage里面处理返回操作 */
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//如果是刷新和加载的请求标识,直接刷新adapter加载数据
if(msg.arg1 == SUCC_CODE && isLoad || isRef){
adapter.notifyDataSetChanged();
}
//否则的话就相当于首次进入加载,先关闭动画,然后把数据加载到RV上
else if(msg.arg1 == SUCC_CODE){
joke_img_load.clearAnimation();
joke_img_load.setVisibility(View.GONE);
adapter = new JokeLVAdapter(activity, datas);
joke_rv.setLayoutManager(new LinearLayoutManager(activity));
joke_rv.setAdapter(adapter);
//当rv的item点击之后进入此方法,并在openWindow处理逻辑
adapter.setLinster(new JokeLVAdapter.ItemOnClickLinster() {
@Override
public void textItemOnClick(View view, int position) {
Log.i("activity", "----->position=" + position);
//打开一个窗口
openWindow(position);
}
});
}else{
//数据加载失败,关闭动画,并提示
joke_img_load.clearAnimation();
joke_img_load.setVisibility(View.GONE);
Toast.makeText(activity, R.string.getDataError, Toast.LENGTH_SHORT).show();
}
}
};
/**
* 通过position去查找唯一的一条信息
* @param position
*/
private void openWindow(int position) {
Toast.makeText(activity, "当前点击item的下标为" + position, Toast.LENGTH_SHORT).show();
}
/** 设置一个集合,用来存储网络请求到的数据 */
List<JokesNew> datas = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//去掉标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initView();
initData();
//修改状态栏颜色
StatusBarCompat.setStatusBarColor(activity, ContextCompat.getColor(activity, R.color.cyan));
}
public void initView() {
//获取控件id
joke_img_back = findViewById(R.id.joke_img_back);
joke_img_load = findViewById(R.id.joke_img_load);
joke_rv = findViewById(R.id.joke_rv);
activity_joke_refreshLayout = findViewById(R.id.activity_joke_refreshLayout);
//设置refreshLayout的一些操作
//越界回弹
activity_joke_refreshLayout.setEnableOverScrollBounce(false);
//在刷新或者加载的时候不允许操作视图
activity_joke_refreshLayout.setDisableContentWhenRefresh(true);
activity_joke_refreshLayout.setDisableContentWhenLoading(true);
//监听列表在滚动到底部时触发加载事件(默认true)
activity_joke_refreshLayout.setEnableAutoLoadmore(false);
/**
* 正在下拉刷新数据中
*/
activity_joke_refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {
Log.i("activity", "下拉刷新");
//数据加载完后调用这行结束刷新
isRef = true;
handler.post(getRefreshDatas);
}
});
/**
* 正在上拉加载数据中
*/
activity_joke_refreshLayout.setOnLoadmoreListener(new OnLoadmoreListener() {
@Override
public void onLoadmore(RefreshLayout refreshlayout) {
Log.i("activity", "上拉加载");
isLoad = true;
handler.post(getLoadmoreDatas);
}
});
//退出
joke_img_back.setOnClickListener(this);
}
public void initData() {
//将xml的控件设置为可见状态,并开启一个动画去过渡加载数据中的空白页面
joke_img_load.setVisibility(View.VISIBLE);
openA(activity, joke_img_load);
//请求
handler.post(getDatas);
}
/**
* getDatas
*/
Runnable getDatas = new Runnable() {
@Override
public void run() {
HttpRequest.get(HTTPURLS + "&key=" + Joke_APPKEY, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i("activity", "数据获取成功");
String result = response.body().string();
JsonDta(result);
}
});
}
};
/**
* 解析json
* @param result
*/
private void JsonDta(String result) {
Message message = handler.obtainMessage();
//解析json数据并赋值给SJJokeNow对象
SJJokeNow obj = new Gson().fromJson(result, SJJokeNow.class);
//不成功时,通知handler数据加载失败
if(obj.getError_code() != 0){
message.arg1 = obj.getError_code();
handler.sendMessage(message);
}else {
//成功时,判断位
if(isRef){
Log.i("activity", "------>" + obj.getReason());
List<JokesNew> json = new ArrayList<>();
for (int i = 0; i < obj.getResult().size(); i ++){
JokesNew info = new JokesNew();
info.setHashId(obj.getResult().get(i).getHashId());
info.setContent(obj.getResult().get(i).getContent());
info.setUnixtime(obj.getResult().get(i).getUnixtime());
json.add(info);
}
for(int i = 0 ; i < datas.size() ; i++) {
json.add(datas.get(i));
}
datas.clear();
for(int i = 0 ; i < json.size() ; i++) {
datas.add(json.get(i));
}
isRef = false;
}else {
Log.i("activity", "------>" + obj.getReason());
for (int i = 0; i < obj.getResult().size(); i ++){
JokesNew info = new JokesNew();
info.setHashId(obj.getResult().get(i).getHashId());
info.setContent(obj.getResult().get(i).getContent());
info.setUnixtime(obj.getResult().get(i).getUnixtime());
datas.add(info);
}
}
message.arg1 = obj.getError_code();
handler.sendMessage(message);
}
}
/**
* 加载刷新的数据
*/
Runnable getRefreshDatas = new Runnable() {
@Override
public void run() {
HttpRequest.get(HTTPURLS + "&key=" + Joke_APPKEY, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
activity_joke_refreshLayout.finishRefresh(0000 , false);
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i("activity", "数据获取成功");
activity_joke_refreshLayout.finishRefresh(0000 , true);
String result = response.body().string();
JsonDta(result);
}
});
}
};
/**
* 加载上拉的数据
*/
Runnable getLoadmoreDatas = new Runnable() {
@Override
public void run() {
HttpRequest.get(HTTPURLS + "&key=" + Joke_APPKEY, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
activity_joke_refreshLayout.finishLoadmore(0000 , false);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i("activity", "数据获取成功");
activity_joke_refreshLayout.finishLoadmore(0000 , true);
String result = response.body().string();
JsonDta(result);
}
});
}
};
@Override
public void onClick(View v) {
int temdId = v.getId();
if(temdId == R.id.joke_img_back){
finish();
}
}
/**
* 开启一个动画
* @param img
*/
public static void openA(Activity activity, ImageView img){
//加载loading动画
rotateAnimation = AnimationUtils.loadAnimation(activity, R.anim.loading);
LinearInterpolator interpolator = new LinearInterpolator();
rotateAnimation.setInterpolator(interpolator);
img.startAnimation(rotateAnimation);
}
}
注:本文的注释写的已经相当清楚了,所以就不多做解释啦。
JokeLVAdapter.class
/**
* 添加数据的适配器
*/
public class JokeLVAdapter extends RecyclerView.Adapter<JokeLVAdapter.ViewHolder> {
/** 上下文 */
Activity context;
/** 数据源 */
List<JokesNew> data;
/** 控件 */
LayoutInflater inflater;
/**
* 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
*/
LruCache<String, BitmapDrawable> mMemoryCache;
/**
* 这里的data作为数据源从activity传入
* @param activity
* @param datas
*/
public JokeLVAdapter(Activity activity, List<JokesNew> datas){
this.context = activity;
this.data = datas;
//获取布局
inflater = LayoutInflater.from(activity);
// 获取应用程序最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, BitmapDrawable>(cacheSize) {
@Override
protected int sizeOf(String key, BitmapDrawable drawable) {
return drawable.getBitmap().getByteCount();
}
};
}
/**
* 加载布局,相当于activity的onCreate方法
* @param parent
* @param viewType
* @return
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.joke_lv_item, parent, false);
return new ViewHolder(view);
}
/**
* 绑定数据
* @param viewHolder
* @param position
*/
@Override
public void onBindViewHolder(final ViewHolder viewHolder, int position) {
viewHolder.joke_lv_txtconent.setText(data.get(position).getContent());
viewHolder.joke_lv_txttime.setText("时间戳:" + data.get(position).getUnixtime() + "");
//设置tag
viewHolder.itemView.setTag(position);
}
/**
* 数据源的内容大小
* @return
*/
@Override
public int getItemCount() {
return data.size();
}
/**
* //自定义的ViewHolder,持有每个Item的的所有界面元素
*/
public class ViewHolder extends RecyclerView.ViewHolder {
/** 获取item的控件 */
public TextView joke_lv_txttime;
public TextView joke_lv_txtconent;
public LinearLayout lin_alljoke;
public ViewHolder(View rootView) {
super(rootView);
this.joke_lv_txtconent = rootView.findViewById(R.id.joke_lv_txtconent);
this.joke_lv_txttime = rootView.findViewById(R.id.joke_lv_txttime);
this.lin_alljoke = rootView.findViewById(R.id.lin_alljoke);
//设置item的点击事件
this.lin_alljoke.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Linster.textItemOnClick(v, getPosition());
}
});
}
}
public ItemOnClickLinster Linster;
public void setLinster(ItemOnClickLinster linster) {
Linster = linster;
}
public interface ItemOnClickLinster{
void textItemOnClick(View view, int position);
}
}
注:同上,注释简单明了,不多废话。继续,下一步:
貌似没有下一步啦,其实吧,整个功能没有难懂的地方,只要理解了代码,换到自己的项目里面能够熟练的使用就可以,可以根据不同的需求去定制不同的实现方式,而这里的这种方式只是一种,给大家借鉴一下而已,同时有什么好方法也可以给我推荐下,代码的不足也可以指出,静等各位大佬佳音!!
附上demo源码,因不太熟练GitHub,所以这里的链接还是csdn的。
本文引用GitHub的刷新框架,不拥有解释权,如果想进一步了解刷新框架,请前往下面的地址阅读
https://github.com/scwang90/SmartRefreshLayout
q:486789970
email:[email protected]
如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。
---财财亲笔