簡介
在安卓開發中,我們會經常遇到上拉加載和下拉刷新的功能,通過網絡請求拿到數據然後添加到控件上,之前自己也在網上搜索過一些文章,但基本上用處不大,要麼是效果不一樣,要麼是貼的代碼不完整,從而導致功能無法正常使用(很蛋疼),所以在摸索了一陣之後,完成了一個大致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]
如果有什麼問題,歡迎大家指導。並相互聯繫,希望能夠通過文章互相學習。
---財財親筆