ListView分頁載入,動態從網上拉取資料
阿新 • • 發佈:2018-12-31
最近做專案有個需求:
(1)從網上獲取分頁資料;
(2)在Android手機端顯示;
(3)載入的動畫和文字;
(4)資料超過40條時顯示滑動條等。
由於之前自己做的偏底層一點,所以這塊內容琢磨了蠻久,最後可以完美實現專案需求,內容見下面:
一、首先,需要有個佈局檔案:activity_story_category.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/rl_title" android:background="#F0F3F7"> <ListView android:id="@+id/lv_category_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="12dp" android:layout_marginStart="12dp" android:layout_marginTop="12dp" android:background="#FFFFFF" android:divider="#F0F3F7" android:dividerHeight="6dp" /> </RelativeLayout>
二、需要有個針對ListView的自定義Adapter:StoryCategoryAdapter.java
public class StoryCategoryAdapter extends BaseAdapter { private List<Map<String, Object>> data = new ArrayList<>(); private Context context; public StoryCategoryAdapter(Context context) { this.context = context; } class Holder { ImageView icon; TextView name; ImageView more; } public void setData(List<Map<String, Object>> data) { this.data = data; } @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @SuppressLint("InflateParams") @Override public View getView(int position, View convertView, ViewGroup parent) { Holder holder; if (convertView == null) { holder = new Holder(); convertView = LayoutInflater.from(context).inflate(R.layout.listview_item_story_category, null); holder.icon = (ImageView) convertView.findViewById(R.id.iv_icon); holder.name = (TextView) convertView.findViewById(R.id.tv_name); holder.more = (ImageView) convertView.findViewById(R.id.iv_more); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); } if (position < getCount()) { Map<String, Object> map = data.get(position); if (map != null && map.size() > 0) { Glide.with(context) .load(map.get("icon")) .into(holder.icon); holder.name.setText((CharSequence) map.get("name")); holder.more.setImageResource(R.drawable.activity_home_me_more); } } return convertView; }
三、還需要載入時的動畫布局和全部資料載入完成後的文字提示佈局:
(1)activity_story_category_loading.java檔案:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="54dp" android:background="#fff0f3f7" android:minHeight="54dp"> <ProgressBar android:id="@+id/progress_bar_loading" android:layout_width="29dp" android:layout_height="29dp" android:layout_centerInParent="true" android:clickable="false" android:focusable="false" android:indeterminateDrawable="@drawable/activity_story_category_progress_loading" android:indeterminateDuration="1500" /> </RelativeLayout>
(2)activity_story_category_loading_text檔案:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="54dp"
android:background="#fff0f3f7"
android:minHeight="54dp">
<TextView
android:id="@+id/tv_loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal"
android:text="@string/activity_story_introduce_end" <!--已經到底了~-->
android:textColor="#ff999999"
android:textSize="12sp" />
</RelativeLayout>
前面三步準備工作做好了,就該正式開工咯。
四、實現ListView分頁載入:StoryCategoryActivity.java
為了讓讀者更有融入感,我決定全盤複製,更易看懂。(注:核心在addData()和onScrollListener )
public class StoryCategoryActivity extends BaseActivity {
private ModuleBean.Data.Module module;
private ModuleBean.Data.Module.Content content;
private ListView lvCategoryItem;
private StoryCategoryAdapter storyCategoryAdapter;
private List<Map<String, Object>> listData = new ArrayList<>();
private View footerView;
private TextView tvTitle;
private int page = 1;
private boolean isScroll = false; // 還在滑動就不載入資料
private boolean isLoadingAll = true;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (AppData.isKillBySystem) {
finish();
return;
}
setContentView(R.layout.activity_story_category);
initView();
initData();
}
@SuppressLint("InflateParams")
private void initView() {
findViewById(R.id.img_arrow_back).setOnClickListener(onClickListener);
findViewById(R.id.iv_history).setOnClickListener(onClickListener);
tvTitle = (TextView) findViewById(R.id.tv_title);
lvCategoryItem = (ListView) findViewById(R.id.lv_category_item);
storyCategoryAdapter = new StoryCategoryAdapter(this);
lvCategoryItem.setAdapter(storyCategoryAdapter);
footerView = LayoutInflater.from(this).inflate(R.layout.activity_story_category_loading, null); // 載入時轉圈動畫布局
lvCategoryItem.addFooterView(footerView); // addFooterView瞭解一下
lvCategoryItem.setOnScrollListener(onScrollListener);
}
private void initData() {
if (getIntent().getExtras() != null) {
switch (getIntent().getIntExtra("tag", 1)) {
case GlobalVar.STORY_MODULE:
module = new Gson().fromJson(getIntent().getStringExtra("bean"), ModuleBean.Data.Module.class);
String name1 = module.name;
if ("".equals(name1) || name1 == null) tvTitle.setText("");
else tvTitle.setText(name1);
getPlayList(page);
break;
case GlobalVar.STORY_SUB_MODULE:
module = new Gson().fromJson(getIntent().getStringExtra("bean"), ModuleBean.Data.Module.class);
String name2 = module.name;
if ("".equals(name2) || name2 == null) tvTitle.setText("");
else tvTitle.setText(name2);
getPlayList(page);
break;
case GlobalVar.STORY_SUB_MODULE_CONTENT:
content = new Gson().fromJson(getIntent().getStringExtra("bean"), ModuleBean.Data.Module.Content.class);
String name3 = content.name;
if ("".equals(name3) || name3 == null) tvTitle.setText("");
else tvTitle.setText(name3);
break;
}
}
}
private void getPlayList(int page) {
new Handler().postDelayed(() -> {
if (listData.size() == 0) {
FuncUtils.toast("網路較差,請重試!");
}
}, 10000);
WebAPIUtils.getPlayList("ZmQ3M2Q1MmFmYWZl", "400010C800000001", "f7109feead0ada9c3f5639a867c788f37858", module.id, page, 20,
this::addData
);
}
private void addData(List<PlayListBean.Data.Content> playList, int total) {
lvCategoryItem.setOnScrollListener(onScrollListener); // 再次註冊監聽
if (listData.size() < 40) { // 設定當資料低於40條時不顯示滑動條
lvCategoryItem.setVerticalScrollBarEnabled(false);
} else {
lvCategoryItem.setVerticalScrollBarEnabled(true); // 設定當資料有40條或者超過40條時顯示滑動條
}
if (!isScroll) {
for (int i = 0; i < playList.size(); i++) {
// LogUtils.i(TAG, "總數量:" + total + "個," + "單次獲取:" + playList.size() + "個," + playList.get(i).id + playList.get(i).name + playList.get(i).imgSmall);
Map<String, Object> map = new HashMap<>();
map.put("icon", playList.get(i).imgSmall);
map.put("name", (this.page - 1) * 20 + i + 1 + "." + playList.get(i).name);
listData.add(map);
}
if ((total - playList.size() - 20 * (this.page - 1)) > 0) { // 分段獲取
this.page++;
} else {
this.page = -1;
}
storyCategoryAdapter.setData(listData);
storyCategoryAdapter.notifyDataSetChanged();
}
}
private AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
int visibleLastIndex = 0; // 最後的可視項索引
int visibleItemCounts; // 當前視窗可見項總數
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
isScroll = true;
break;
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
isScroll = true;
break;
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
int itemsLastIndex = storyCategoryAdapter.getCount() - 1; // 資料集最後一項的索引
int lastIndex = itemsLastIndex + 1; // 加上底部的loadMoreView項
if (visibleLastIndex == lastIndex) {
isScroll = false;
LogUtils.i(TAG, "##### 滾動到底部 ######");
if (page >= 1) {
lvCategoryItem.setOnScrollListener(null); // 訪問網路時,設定滑動監聽無效,解決非同步獲取資料時產生的資料重複問題
getPlayList(page);
} else {
LogUtils.i(TAG, "沒有更多資料了!");
if (isLoadingAll) {
lvCategoryItem.removeFooterView(footerView);
lvCategoryItem.addFooterView(LayoutInflater.from(StoryCategoryActivity.this).inflate(R.layout.activity_story_category_loading_text, null));
isLoadingAll = false;
}
}
}
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.visibleItemCounts = visibleItemCount;
visibleLastIndex = firstVisibleItem + visibleItemCount - 1;
}
};
private View.OnClickListener onClickListener = v -> {
switch (v.getId()) {
case R.id.img_arrow_back:
finish();
break;
case R.id.iv_history:
startActivity(new Intent(this, StoryHistoryActivity.class));
break;
}
};
}
當然,我也遇到了蠻多問題,比如:
(1)如果給listview設定了setOnItemClickListener()監聽,一定注意,點選底部載入動畫和非底部item時處理不一樣。像我不想處理點選底部,try-catch一下避免app崩掉就可以了!
(2)底部載入動畫和載入文字的切換標準,我的內容主要是用了兩個變數來判斷,你可以看看程式碼。
(3)listview從網上拉取資料時,手指還在滑動,這個時候資料狀態會出現問題,因為是非同步獲取資料,具體解決辦法可以參見程式碼。
總結:
遇到問題不可怕,多去網上找找,多想想,總能解決的!
走一段令人留戀的路,做一個不負自己的人。——共勉