程序员人生 网站导航

Android中listView的下拉加载功能实现

栏目:综合技术时间:2016-07-05 14:50:13
  • 今天给大家讲讲android开发中比较常见的listView的下拉加载,其实也能够叫做分页加载。为何会有这个叫法呢?说说我的理解吧!

    • 从字面上很好理解。当你滑动1个列表到底部的时候,这个时候就会出现正在加载的底部加载布局去加载更多的数据。这里拿微信作为1个例子,以下图所示:
    • 这里写图片描述

    • 如上图所示红色方框的部份就是底部的加载布局。从1定程度上这样做是必要的,为了优化用户的体验。你可以想一想,假设你点开微信的朋友圈的时候,如果没有做分页加载,那末你需要等待很久的时间才能够看到你和朋友发的1些状态。这是由于这个时候代码需要给服务器发送要求获得数据并显现这些数据。这个时候获得数据就成了最耗时的问题。为何这么说?如果没有分页加载的功能,那末,你肯定需要获得全部的数据,你可以想一想,好几年的数据,数据量可很多啊!发要求去获得这些数据并显现它们是会很耗时间的。所以分页加载就很好的解决了这个问题。你先获得其中的10几条数据并显现它们。当你1直从头阅读这10几条数据到结束的时候,这个时候再去加载更多的数据并显现它们。如上图所示,这样才是好的用户体验。


  • 概念清晰后,具体实现的部份到了。这里实现微信的下拉加载效果(个人感觉很不错,简洁明了),大概需要以下几个步骤:
    • first — 首先构建下拉加载时的底部加载布局(这里命名为view_more)
    • second — 获得数据并给listView设置adapter;设置好adapter后,调用方法:listView.addFooterView(view_more);// TODO 添加底部记载布局再往listView的底部添加底部加载布局。最后对listView设置滑动监听器:listView.setOnScrollListener(this);// TODO listView这是滑动监听 并重写相干方法:
      public void onScrollStateChanged(AbsListView view, int scrollState) {}public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {} 这两个重写的方法1个是监听listView的滑动状态改变,另外一个是监听listView的滑动。
    • third — 当滑动监听设置好后,在重写的两个方法里面判断listView是不是滑动到底部并且停止滑动(即滑动状态为停止滑动)。如果此时满足这个条件,那末就加载更多的数据。数据加载完成后就刷新数据源,此时会有更多的数据在底部出现。
    • fourth — 最后当数据全部加载好后,移除第2步中添加的底部加载布局view_more,调用方法:listView.removeFooterView(view_more);// TODO 移除底部的加载布局

下面写代码1步1步来实现:

  • 1、首先准备1个底部加载布局,命名为view_more
<?xml version="1.0" encoding="utf⑻"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="80dp" android:background="#8888" > <TextView android:id="@+id/tv_Load" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="正在加载..." android:textSize="14sp" android:visibility="visible" /> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toLeftOf="@id/tv_Load" android:padding="10dp" android:visibility="visible" /> </RelativeLayout>

很简单的布局,显示了1个进度条和1个文本控件。如图:

这里写图片描述

  • 第2,给listView设置数据并设置滑动监听,设置好后判断是不是滑动到listView的底部并停止滑动,如果是那末加载更多的数据:
package com.example.drop_down_load; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; public class MainActivity extends Activity implements OnScrollListener { private ListView listView; private int totalCount;// 数据总条数 private List<String> lists = new ArrayList<String>(); private ArrayAdapter<String> adapter; // 创建handler接收消息并处理消息 private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0: // 创建adapter adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, lists); // 设置adapter listView.setAdapter(adapter); // 添加底部加载布局 listView.addFooterView(view_more); // 设置监听 setListeners(); break; } }; }; private View view_more; private ProgressBar pb; private TextView tvLoad; private int lastVisibleIndex; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 控件初始化 initViews(); // 初始化数据 initData(); } private void initData() { // 摹拟网络要求获得数据,1次获得15条 new Thread() { public void run() { try { totalCount = 100;// 假定数据1共有100条,将来调接口可以获得到这个值 for (int i = 0; i < 15; i++) { lists.add("数据" + (i + 1)); } // 给handler发消息更新UI,子线程不可以更新UI Message message = new Message(); message.what = 0; handler.sendMessage(message); } catch (Exception e) { e.printStackTrace(); } }; }.start(); } private void setListeners() { if (totalCount > 15) { // listView设置滑动简监听 listView.setOnScrollListener(this); } else { // 假设数据总数少于等于15条,直接移除底部的加载布局,不需要再加载更多的数据 listView.removeFooterView(view_more); } } private void initViews() { listView = (ListView) findViewById(R.id.listView); // 构建底部加载布局 view_more = (View) getLayoutInflater() .inflate(R.layout.view_more, null); // 进度条 pb = (ProgressBar) view_more.findViewById(R.id.progressBar); // “正在加载...”文本控件 tvLoad = (TextView) view_more.findViewById(R.id.tv_Load); } /** * 监听listView的滑动状态的改变 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { Log.e("TAG", "lastVisibleIndex = " + lastVisibleIndex); Log.e("TAG", "adapter.getCount() = " + adapter.getCount()); // 滑到底部后自动加载,判断listView已停止转动并且最后可视的条目等于adapter的条目 // 注意这里在listView设置好adpter后,加了1个底部加载布局。 // 所以判断条件为:lastVisibleIndex == adapter.getCount() if (scrollState == SCROLL_STATE_IDLE && lastVisibleIndex == adapter.getCount()) { /** * 这里也要设置为可见,是由于当你真正从网络获得数据且获得失败的时候。 * 我在失败的方法里面,隐藏了底部的加载布局并提示用户加载失败。所以再次监听的时候需要 * 继续显示隐藏的控件。由于我摹拟的获得数据,失败的情况这里不给出。实际中简单的加上几句代码就好了。 */ pb.setVisibility(View.VISIBLE); tvLoad.setVisibility(View.VISIBLE); loadMoreData();// 加载更多数据 } } private void loadMoreData() { } /** * 监听listView的滑动 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // 计算最后可见条目的索引 lastVisibleIndex = firstVisibleItem + visibleItemCount - 1; // 当adapter中的所有条目数已和要加载的数据总条数相等时,则移除底部的View if (totalItemCount == totalCount + 1) { // 移除底部的加载布局 listView.removeFooterView(view_more); } } }

以上代码就是我上面步骤2和步骤2中所说的东西。加上我写了注释,所以相信大家看起来很简单。关键点是怎样判断listView是不是滑动到了底部并停止了滑动。我在代码中添加了两行log,可以打印日志信息。大家去自己调试就可以明白我对关键点是怎样判断的了,但是也有聪明的!嘿嘿!大家都懂,自己人,我就不接话了。如图:

这里写图片描述

这里写图片描述

  • 最后就是加载更多的数据了,即完成第2步中未完成的方法:
loadMoreData();// 加载更多数据

具体代码实现为:

private void loadMoreData() { // 获得此时adapter中的总条目数 int count = adapter.getCount(); // 1次加载15条数据,即下拉加载的履行 if (count + 15 < totalCount) { start = count; end = start + 15; initData(start, end);// 摹拟网络获得数据操作 } else {// 数据不足15条直接加载到结束 start = count; end = totalCount; initData(start, end);// 摹拟网络获得数据曹祖 // 数据全部加载完成后,移除底部的view listView.removeFooterView(view_more); Toast.makeText(MainActivity.this, "数据已全部加载", 1).show(); } } private void initData(final int start, final int end) { // 摹拟网络要求获得数据,1次获得15条 new Thread() { public void run() { try { Thread.sleep(4000);// 摹拟获得数据时的耗时3s for (int i = start; i < end; i++) { lists.add(i, "数据" + (i + 1)); } // 给handler发消息更新UI,子线程不可以更新UI Message message = new Message(); message.what = 1; handler.sendMessage(message); } catch (Exception e) { e.printStackTrace(); } }; }.start(); }

此时handler中还要再加1个case语句,用来刷新数据源,如图红色方框标注地方,大家自己加1下。最后我会给项目源码,大家可以下载看我的代码:

这里写图片描述

好了下面给出几张项目的展现图片:

  • 15条数据后加载更多数据,如图:

这里写图片描述

  • 30条数据后加载更多数据,如图:

这里写图片描述

  • 45条数据后加载更多数据,如图:

这里写图片描述

  • 数据全部加载完后,移除底部加载布局,如图:

这里写图片描述

谈谈我的感悟:你需要有自己的想法思路,他人的东西可以鉴戒,但是你需要从中学到点甚么。要是甚么都没学到,那就失去了分享的意义了!分享是为了让大家学到更多,收获更多。希望我的这篇能够给大家1点点收获!

项目的源码下载地址:点击我下载项目源码

每天进步1点点!加油!

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐