我们之前讨论了ListView的基本使用方法ListView的优化

  今天我们再来讨论一个关于ListView的一个新的东西~就是分页加载。那么什么是分页加载呢?简单点说,就是“下拉刷新”

我们来简单的构思一下我们都需要些什么东西

  首先,我们需要主界面布局,并且在布局文件中写一个ListView

  然后,我们需要ListView中每个item的布局

  然后,我们还需要一个当页面加载的时候出现的那个旋转的小圆圈(ProgressBar)的布局,我们可以将其称之为“底部布局”(自己起的名字)。

  然后,我们需要一个Activity,在其中我们需要设置数据源和一系列操作。

  然后,我们需要一个类,该类代表每个item,存储的是每个item中的内容

接下来,我们来按照我们上面的简单的构思,来做一个具体点的构思

  主界面布局文件我就不用多说了,就是里面添加一个ListView

  然后,我们来考虑item中的布局,我们想显示一个头像,一个名字,一个内容。我们就定义一个ImageView、一个TextView(管名字)、一个TextView(管内容)

  然后,我们需要在底部布局中,添加一个ProgressBar (就是旋转小圆圈,又叫圆形进度条)和一个TextView(用来写“正在加载~”字样)。

  然后,我们需要一个代表item中的内容,由于我们上面已经说了,我们想显示一个头像,一个名字,一个内容。 那么,我们在该类中就需要维护一个int类型的icon(图片是int类型的),一个String类型的title(名字/标题),一个String类型的content(内容)

  然后,我们需要Acitivty,在Activity中,我们需要定义数据源,和一个适配器,还有一个存储对象的list集合。并且让Activity实现OnScrollListener接口,并实现其中的两个方法 1.onScroll方法 2.onScrollStateChanged方法

我们根据上面的构思,来做具体的实现

  我们先来看我们的主布局文件

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"
  3. android:layout_width="match_parent" android:layout_height="match_parent"
  4. android:paddingBottom="@dimen/activity_vertical_margin"
  5. android:paddingLeft="@dimen/activity_horizontal_margin"
  6. android:paddingRight="@dimen/activity_horizontal_margin"
  7. android:paddingTop="@dimen/activity_vertical_margin"
  8. tools:context="application.smile.listview.MainActivity">
  9.  
  10. <ListView
  11. android:id="@+id/listView"
  12. android:layout_width="match_parent"
  13. android:layout_height="match_parent"/>
  14. </RelativeLayout>

  然后,我们来看每个item中的布局

  

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical" android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:padding="10dp">
  6. <ImageView
  7. android:id="@+id/iv_icon"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:padding="5dp"
  11. android:src="@mipmap/ic_launcher"/>
  12. <TextView
  13. android:id="@+id/tv_title"
  14. android:layout_marginTop="10dp"
  15. android:layout_toRightOf="@+id/iv_icon"
  16. android:layout_width="match_parent"
  17. android:layout_height="wrap_content"
  18. android:text="这是标题"
  19. android:textSize="20sp"/>
  20. <TextView
  21. android:id="@+id/tv_content"
  22. android:layout_toRightOf="@+id/iv_icon"
  23. android:layout_below="@+id/tv_title"
  24. android:layout_width="match_parent"
  25. android:layout_height="wrap_content"
  26. android:layout_marginLeft="10dp"
  27. android:text="这是内容"/>
  28. </RelativeLayout>

  就是这样的效果:

    

  接下来,我们来看我们底部布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="horizontal" android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:gravity="center">
  6.  
  7. <ProgressBar
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content" />
  10. <TextView
  11. android:layout_width="wrap_content"
  12. android:layout_height="wrap_content"
  13. android:text="拼命加载中"/>
  14. </LinearLayout>

  看到的效果就是这样的:

    

  这样,我们的布局文件就到这了。

接下来,我们先来看创建的item的类

  1. public class ItemContent {
  2. int icon;
  3. String title ;
  4. String content;
  5. }

然后,我们再来看Activity中的实现

  这里是最重要的了……因为所有的实现都在Activity中,我们还是有必要再细一些的讲解

  首先,我们需要实现接口,然后设置数据源(就是图片和名字),创建一个list集合用来存放刚刚创建的类的对象(就是ItemContent),再维护一个适配器

  然后,我们在onCreate(...)方法中去执行我们的老套路找到控件、然后更新数据、然后设置适配器,然后设置点击事件(这里有两个事件,一个是滚动事件,一个是点击事件),这里还要加一个,就是得到我们的底部布局,并加入到ListView中

  然后,我们在更新数据的方法中去设置每次加载多少条数据。就是通过一个循环来控制每次加载多少条数据,每次都创建一个ItemContent(创建的item类)的对象,并为其赋值,最后添加到List集合中,这里需要做一个操作,因为我们是模拟,所以没有很多的数据,那么我们就需要让我们现有的数据来循环使用,我们需要一个控制变量(相当于一个指针),当这个控制变量的大小和数据数组的长度一致的时候,就将控制变量置零,让我们从数据数组的开始继续读取,从而实现数据的循环使用。

  然后我们来看我们重写的两个方法中的onScroll方法

    该方法用于监听屏幕滚动

    该方法中有四个参数

      第一个参数AbsListView类型的view   该参数是正在滚动的视图

      第二个参数int类型的firstVisibleItem 该参数是第一个可见的item的索引

      第三个参数int类型的visibleItemCount 该参数是可见的item的数量

      第四个参数int类型的totalItemCount 该参数是列表适配器中的项目数(我们这里用不到)

    然后,我们来看这个方法中,我们需要做些什么

      我们需要定义一个int类型的变量,用来标记我们能看见的最后一个item的索引 ,然后我们通过<“第一个可见的item的索引” + “可见的item的数量” -1> 来获得我们想要的最后一个item的索引

      这里为什么要-1呢?我们来仔细的考虑一下。所谓索引也就是下标,我们知道下标是从0开始的,如果我们不-1 ,我们就会多得到一条数据,每次就不是10个了,而是11个。所以,我们需要-1 来控制,我们每次都拿到10条数据

    这样,我们这个方法就完成了。

  然后,我们来看重写的两个方法中的onScrollStateChanged方法

    该方法用于监听屏幕滚动的状态

    该方法中有两个参数

      第一个参数:AbsListView类型的view  该参数是正在滚动的视图

      第二个参数:int类型的scrollState   该参数是当前滚动的状态

        该参数,可以有三个值与之匹配

            1.SCROLL_STATE_FLING  代表:用户已经使用触摸来滚动,手指已经离开屏幕,但是视图还在惯性的滚动

            2.SCROLL_STATE_IDLE  代表:视图不滚动,并且手指松开

            3.SCROLL_STATE_TOUCH_SCROLL  代表:用户正在使用触摸来滚动,并且手指还在屏幕上

    然后,我们来看这个方法中,我们需要做些什么

      我们需要通过线程来模拟加载的时间,但是在此之前,我们需要先判断“滚动的状态” 是不是处于“视图不再滚动并且手指松开的状态”并且判断能看到的最后一条item的索引是不是适配器的长度

      如果这两个条件都满足,证明已经滚动到了最下面一条,需要更新数据

      于是我们开启一个线程并且睡上1秒,睡完要更新数据,并且显示到ListView上,但是在安卓中想要更新UI就必须在UI线程中就行,不可以在子线程中进行,那么我们想要个更新UI就用上了Handler 这个类

      我们要在成员位置创建一个Handler对象,并重写handleMessage方法,在方法中做更新UI的操作,那么我们如何让子线程与HandleMessage方法链接上呢?我们需要在子线程中通过Handler对象,去发送一个一个消息,并附上一个标志

      然后,我们在HandleMessage中去判断这个标志,如果成功就更新UI

  接下来,我们就来看我们自定义的适配器。还是之前的套路,继承BaseAdapter,并重写方法

    再重写方法之前,我们需要维护一个布局填充器,并在构造方法中给他赋值

    然后我们来看重写的方法

      第一个方法getCount方法   这个需要返回总共有多少个item,这里就是list集合的size

      第二个方法:getItem方法  这个就是返回每个item,这里就是通过list集合的get方法获得item并返回

      第三个方法:getItemId方法 这个就是返回每个item的id,这里就是直接返回position

      第四个方法:getView方法 这个方法中就是具体的情况了

        首先创建一个类取名为ViewHolder并在其中声明item中的三个控件

        然后在getView方法中维护一个ViewHolder

        然后判断convetView是否为空,如果为空就通过布局填充器获得item的布局。

          并实例化ViewHolder,通过convertView找到控件并赋值给ViewHolder的相应的控件。

          然后设置一个标记,把ViewHolder放进去。

        如果不为空,就通过标记取出ViewHolder,并给每个空间设置数据源。

        最后返回convertView

然后,我们来上代码:

  1. public class MainActivity extends AppCompatActivity implements AbsListView.OnScrollListener {
  2. //用来判断点击的是不是“正在加载”的item,true代表是,false代表不是
  3. private boolean flag = false;
  4. //用于Handler返回的标志
  5. private static final int SEND_OK = 0X1;
  6. //存储文字的数组
  7. private String[] names = {"郭嘉", "黄月英", "华佗", "刘备", "陆逊", "吕布", "吕蒙", "马超", "司马懿", "孙权",
  8. "孙尚香", "夏侯惇", "许褚", "杨修", "张飞", "赵云", "甄姬", "周瑜", "诸葛亮"};
  9. private List<ItemContent> project = new ArrayList<>();
  10. //存储图片的数组
  11. private int[] images = {R.mipmap.guojia, R.mipmap.huangyueying, R.mipmap.huatuo, R.mipmap.liubei, R.mipmap.luxun, R.mipmap.lvbu, R.mipmap.lvmeng,
  12. R.mipmap.machao, R.mipmap.simayi, R.mipmap.sunquan, R.mipmap.sunshangxiang, R.mipmap.xiahoudun, R.mipmap.xuchu, R.mipmap.yangxiu,
  13. R.mipmap.zhangfei, R.mipmap.zhaoyun, R.mipmap.zhenji, R.mipmap.zhouyu, R.mipmap.zhugeliang};
  14. private MyAdapter myAdapter;
  15. //创建一个Handler对象
  16. private Handler handler = new Handler() {
  17. //当子线程调用sendMessage方法时会自动调用该方法
  18. @Override
  19. public void handleMessage(Message msg) {
  20. super.handleMessage(msg);
  21. //根据标志做相应的操作
  22. switch (msg.what) {
  23. case SEND_OK:
  24. //更新ui
  25. myAdapter.notifyDataSetChanged();
  26. //将“正在加载”的标志,设置为没在加载
  27. flag = false;
  28. break;
  29. }
  30. }
  31. };
  32.  
  33. @Override
  34. protected void onCreate(Bundle savedInstanceState) {
  35. super.onCreate(savedInstanceState);
  36. setContentView(R.layout.activity_main);
  37. //找到控件
  38. ListView listView = (ListView) findViewById(R.id.listView);
  39. //更新数据
  40. initData();
  41. //找到底部布局
  42. View bottom_layout = getLayoutInflater().inflate(R.layout.bottom_layout, null);
  43. //设置到listView上
  44. listView.addFooterView(bottom_layout);
  45.  
  46. //设置适配器
  47. myAdapter = new MyAdapter(this);
  48. listView.setAdapter(myAdapter);
  49.  
  50. listView.setOnScrollListener(this);
  51. //设置点击事件
  52. listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
  53. @Override
  54. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  55. //弹出吐司
  56. if (!flag) {
  57. //如果点击的不是“正在加载”就弹出这个吐司
  58. Toast.makeText(MainActivity.this, "点我了?我是" + names[position % 19], Toast.LENGTH_SHORT).show();
  59. } else {
  60. //如果点击的是“正在加载”就弹出这个吐司
  61. Toast.makeText(MainActivity.this, "加载中....", Toast.LENGTH_SHORT).show();
  62. }
  63. }
  64. });
  65. }
  66. //控制数据循环的控制变量
  67. int count = 0;
  68.  
  69. public void initData() {
  70. //通过循环控制每次获取10条数据
  71. for (int i = 0; i < 10; i++) {
  72. //实例化一个item的类的对象
  73. ItemContent item = new ItemContent();
  74. //判断,如果控制变量的长度大于或者等于数据数组的长度,就置零
  75. if (count >= images.length) {
  76. count = 0;
  77. }
  78. //赋值
  79. item.icon = images[count];
  80. item.title = names[count];
  81. item.content = "我是" + names[count];
  82. //添加到list集合
  83. project.add(item);
  84. //控制变量自增
  85. count++;
  86. }
  87. }
  88.  
  89. /**
  90. * 用于监听ListView滚动状态的变化
  91. *
  92. * @param view 正在报告滚动状态的视图
  93. * @param scrollState 当前的滚动状态
  94. */
  95. @Override
  96. public void onScrollStateChanged(AbsListView view, int scrollState) {
  97. if (!flag) {
  98. /**
  99. *SCROLL_STATE_FLING: 用户已经使用触摸来滚动,手指已经离开屏幕,但是视图还在惯性的滚动
  100. *SCROLL_STATE_IDLE: 视图不滚动,并且手指松开
  101. * SCROLL_STATE_TOUCH_SCROLL: 用户正在使用触摸来滚动,并且他们的手指仍在屏幕上
  102. */
  103. if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && visibleLastItem == myAdapter.getCount()) {
  104.  
  105. new Thread(new Runnable() {
  106. @Override
  107. public void run() {
  108. try {
  109. Thread.sleep(1000);
  110. } catch (InterruptedException e) {
  111. e.printStackTrace();
  112. }
  113. initData();
  114. handler.sendEmptyMessage(SEND_OK);
  115. }
  116. }).start();
  117. flag = true;
  118. }
  119. }
  120. }
  121.  
  122. private int visibleLastItem = 0; //能看到的最后一个单元格的索引
  123.  
  124. /**
  125. * 用于监听ListView屏幕滚动
  126. *
  127. * @param view 正在报告滚动状态的视图
  128. * @param firstVisibleItem 第一个可见单元格的索引
  129. * @param visibleItemCount 可见单元格的数量
  130. * @param totalItemCount 列表适配器中的项目数
  131. */
  132. @Override
  133. public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  134. //最后能看到的视图索引 = 第一个可以看见的单元格索引 + 可见单元格的数量 - 1;
  135. //因为是从0开始的,但是数量是从1开始的,所以 需要减一才可以做索引
  136. visibleLastItem = firstVisibleItem + visibleItemCount -1;
  137. }
  138.  
  139. /**
  140. * 自定义适配器,继承BaseAdapter
  141. */
  142. class MyAdapter extends BaseAdapter {
  143. //维护一个布局填充器,为了得到每个item 的布局
  144. private LayoutInflater layoutInflater;
  145.  
  146. public MyAdapter(Context context) {
  147. //通过构造方法获取布局填充器对象
  148. layoutInflater = LayoutInflater.from(context);
  149. }
  150.  
  151. //该方法是总共有多少个item
  152. @Override
  153. public int getCount() {
  154. return project.size();
  155. }
  156.  
  157. //该方法是得到每个item的值
  158. @Override
  159. public Object getItem(int position) {
  160. return project.get(position);
  161. }
  162.  
  163. //该方法是得到每个item的id
  164. @Override
  165. public long getItemId(int position) {
  166. return position;
  167. }
  168.  
  169. //该方法是获得视图,也是这些里面最重要的方法
  170. @Override
  171. public View getView(int position, View convertView, ViewGroup parent) {
  172. ViewHolder viewHolder;
  173. if (convertView == null) {
  174. //首先,我们通过布局填充器获得item的布局
  175. convertView = layoutInflater.inflate(R.layout.item_layout, null);
  176. //实例化ViewHolder
  177. viewHolder = new ViewHolder();
  178. //根据item的布局找到图片
  179. viewHolder.iv_icon = (ImageView) convertView.findViewById(iv_icon);
  180. //根据item的布局找到标题
  181. viewHolder.tv_title = (TextView) convertView.findViewById(tv_title);
  182. //根据item的布局找到内容
  183. viewHolder.tv_content = (TextView) convertView.findViewById(tv_content);
  184. //设置tag标记
  185. convertView.setTag(viewHolder);
  186. }
  187. viewHolder = (ViewHolder) convertView.getTag();
  188. //设置图片源
  189. viewHolder.iv_icon.setImageResource(project.get(position).icon);
  190. //设置标题文字
  191. viewHolder.tv_title.setText(project.get(position).title);
  192. //设置内容文字
  193. viewHolder.tv_content.setText(project.get(position).content);
  194. //返回item的布局
  195. return convertView;
  196. }
  197.  
  198. class ViewHolder {
  199. ImageView iv_icon;
  200. TextView tv_title;
  201. TextView tv_content;
  202. }
  203. }
  204. }

接下来,我们来看一下运行的结果吧~

  

这样,我们的ListView的分页加载就完成了

让程序写入生命,将代码融入灵魂

                    -------smile、zj

Android基本控件之listView(三)<用ListView实现分页加载>的更多相关文章

  1. ListView上拉刷新和分页加载完整的Dome

    很多人工作的过程中都会碰到ListView下拉刷新和分页加载,然后大多数公司都已经把框架写好了,大家直接用就可以了,有些人一直对这个事情处于迷茫状态,为了让大家对上拉刷新和分页加载有一个比较全面的认识 ...

  2. 实现winform DataGridView控件判断滚动条是否滚动到当前已加载的数据行底部

    判断 DataGridView控件滚动条是否滚动到当前已加载的数据行底部,其实方法很简单,就是为DataGridView控件添加Scroll事件,然后写入以下代码就可以了,应用范围:可实现分部加载数据 ...

  3. winform DataGridView控件判断滚动条是否滚动到当前已加载的数据行底部 z

    http://www.zuowenjun.cn/post/2015/05/20/162.html 判断 DataGridView控件滚动条是否滚动到当前已加载的数据行底部,其实方法很简单,就是为Dat ...

  4. 【MFC】picture控件 两种有细微差别的动态加载图片方法

    摘自:http://www.jizhuomi.com/software/193.html VS2010/MFC编程入门之二十七(常用控件:图片控件Picture Control) 分类标签: 编程入门 ...

  5. DataGridView控件判断滚动条是否滚动到当前已加载的数据行底部

    private void dgvLoad_Scroll(object sender, ScrollEventArgs e) { if (e.ScrollOrientation == ScrollOri ...

  6. WinForm完美实现Cefsharp-v49控件C#与JS交互,并且可加载运行flash

    https://blog.csdn.net/zhao331863874/article/details/117328415

  7. Android:控件ListView列表项与适配器结合使用

    Listview是用来展示一些重复性的数据用的,比如一些列表集合数据展示到手机,需要适配器作为载体获取数据,最后将数据填充到布局. ListView里面的每个子项Item可以使一个字符串,也可以是一个 ...

  8. android中ListView控件&&onItemClick事件中获取listView传递的数据

    http://blog.csdn.net/aben_2005/article/details/6592205 本文转载自:android中ListView控件&&onItemClick ...

  9. Android基础控件ListView基础操作

    1.简介 基于Android基础控件ListView和自定义BaseAdapter适配器情况下,对ListView的数据删除和添加操作: public boolean add(E e) {//添加数据 ...

随机推荐

  1. MapSearch 阅读随笔

    MapSearch https://developer.apple.com/library/ios/samplecode/MapSearch/Introduction/Intro.html#//app ...

  2. dom 动态生产表格

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  3. NovaMind *的安装、和谐破解到永久使用

    XMind *思维导图的安装步 同类型的软件,这两款软件: XMind 和 NovaMind,各有所长.建议,都安装,合适的时候方便使用. XMind界面如下: NovaMind界面如下: 本博文,主 ...

  4. 【多线程】JAVA多线程和并发基础面试问答(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  5. ZOJ 3810 Pretty Poem 分类: ACM 2015-05-17 14:40 83人阅读 评论(0) 收藏

    Pretty Poem Time Limit: 2 Seconds     Memory Limit:65536 KB Poetry is a form of literature that uses ...

  6. 您的IP不在有效范围 ip:port为 [10.15.22.15]

  7. Umbraco部署到IIS中权限问题(back office没有权限新建template)

    在开发项目中,发现把基于Umbraco平台开发的网站部署到服务器的IIS之后,访问该网站的back office 在back office中增加一个template时,发送错误,提示 Access t ...

  8. Cocos2d-x 关于在iOS平台真机测试的一些注意

    下面简单记录一下在最近cocos2d-x项目在iOS平台真机测试和模拟器测试中遇到的一些要注意的地方(使用ipod): 1.图片大小 游戏中基本上都是会用到图片,那么在使用图片的时候要特别注意图片的s ...

  9. 设置UIButton文字大小颜色不同

    _loginBtn = [[UIButton alloc]initWithFrame:CGRectMake(iconX, CGRectGetMaxY(passwordBGView.frame)+25, ...

  10. fastcgi 分布式

    以lighttpd fastcgi写一下自己对fastcgi分布式的理解. 假设一台机器A上运行lighttpd,在这台主机上只是对请求进行分发. 而在其他多台机器上运行多个fastcgi进程,用来接 ...