我们之前讨论了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方法

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

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="application.smile.listview.MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

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

  

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">
    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:src="@mipmap/ic_launcher"/>
    <TextView
        android:id="@+id/tv_title"
        android:layout_marginTop="10dp"
        android:layout_toRightOf="@+id/iv_icon"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="这是标题"
        android:textSize="20sp"/>
    <TextView
        android:id="@+id/tv_content"
        android:layout_toRightOf="@+id/iv_icon"
        android:layout_below="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="这是内容"/>
</RelativeLayout>

  就是这样的效果:

    

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拼命加载中"/>
</LinearLayout>

  看到的效果就是这样的:

    

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

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

public class ItemContent {
    int icon;
    String title ;
    String content;
}

然后,我们再来看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

然后,我们来上代码:

public class MainActivity extends AppCompatActivity implements AbsListView.OnScrollListener {
            //用来判断点击的是不是“正在加载”的item,true代表是,false代表不是
    private boolean flag = false;
            //用于Handler返回的标志
    private static final int SEND_OK = 0X1;
    //存储文字的数组
    private String[] names = {"郭嘉", "黄月英", "华佗", "刘备", "陆逊", "吕布", "吕蒙", "马超", "司马懿", "孙权",
            "孙尚香", "夏侯惇", "许褚", "杨修", "张飞", "赵云", "甄姬", "周瑜", "诸葛亮"};
    private List<ItemContent> project = new ArrayList<>();
    //存储图片的数组
    private int[] images = {R.mipmap.guojia, R.mipmap.huangyueying, R.mipmap.huatuo, R.mipmap.liubei, R.mipmap.luxun, R.mipmap.lvbu, R.mipmap.lvmeng,
            R.mipmap.machao, R.mipmap.simayi, R.mipmap.sunquan, R.mipmap.sunshangxiang, R.mipmap.xiahoudun, R.mipmap.xuchu, R.mipmap.yangxiu,
            R.mipmap.zhangfei, R.mipmap.zhaoyun, R.mipmap.zhenji, R.mipmap.zhouyu, R.mipmap.zhugeliang};
    private MyAdapter myAdapter;
    //创建一个Handler对象
    private Handler handler = new Handler() {
            //当子线程调用sendMessage方法时会自动调用该方法
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
                //根据标志做相应的操作
            switch (msg.what) {
                case SEND_OK:
                        //更新ui
                    myAdapter.notifyDataSetChanged();
                        //将“正在加载”的标志,设置为没在加载
                    flag = false;
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //找到控件
        ListView listView = (ListView) findViewById(R.id.listView);
        //更新数据
        initData();
        //找到底部布局
        View bottom_layout = getLayoutInflater().inflate(R.layout.bottom_layout, null);
        //设置到listView上
        listView.addFooterView(bottom_layout);

        //设置适配器
        myAdapter = new MyAdapter(this);
        listView.setAdapter(myAdapter);

        listView.setOnScrollListener(this);
        //设置点击事件
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //弹出吐司
                if (!flag) {
                        //如果点击的不是“正在加载”就弹出这个吐司
                    Toast.makeText(MainActivity.this, "点我了?我是" + names[position % 19], Toast.LENGTH_SHORT).show();
                } else {
                        //如果点击的是“正在加载”就弹出这个吐司
                    Toast.makeText(MainActivity.this, "加载中....", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
    //控制数据循环的控制变量
    int count = 0;

    public void initData() {
            //通过循环控制每次获取10条数据
        for (int i = 0; i < 10; i++) {
                //实例化一个item的类的对象
            ItemContent item = new ItemContent();
                //判断,如果控制变量的长度大于或者等于数据数组的长度,就置零
            if (count >= images.length) {
                count = 0;
            }
                //赋值
            item.icon = images[count];
            item.title = names[count];
            item.content = "我是" + names[count];
                //添加到list集合
            project.add(item);
                //控制变量自增
            count++;
        }
    }

    /**
     * 用于监听ListView滚动状态的变化
     *
     * @param view        正在报告滚动状态的视图
     * @param scrollState 当前的滚动状态
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (!flag) {
            /**
             *SCROLL_STATE_FLING:   用户已经使用触摸来滚动,手指已经离开屏幕,但是视图还在惯性的滚动
             *SCROLL_STATE_IDLE:    视图不滚动,并且手指松开
             * SCROLL_STATE_TOUCH_SCROLL:   用户正在使用触摸来滚动,并且他们的手指仍在屏幕上
             */
            if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && visibleLastItem == myAdapter.getCount()) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        initData();
                        handler.sendEmptyMessage(SEND_OK);
                    }
                }).start();
                flag = true;
            }
        }
    }

    private int visibleLastItem = 0;  //能看到的最后一个单元格的索引

    /**
     * 用于监听ListView屏幕滚动
     *
     * @param view             正在报告滚动状态的视图
     * @param firstVisibleItem 第一个可见单元格的索引
     * @param visibleItemCount 可见单元格的数量
     * @param totalItemCount   列表适配器中的项目数
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        //最后能看到的视图索引 = 第一个可以看见的单元格索引 +  可见单元格的数量 - 1;
        //因为是从0开始的,但是数量是从1开始的,所以 需要减一才可以做索引
        visibleLastItem = firstVisibleItem + visibleItemCount -1;
    }

    /**
     * 自定义适配器,继承BaseAdapter
     */
    class MyAdapter extends BaseAdapter {
        //维护一个布局填充器,为了得到每个item 的布局
        private LayoutInflater layoutInflater;

        public MyAdapter(Context context) {
            //通过构造方法获取布局填充器对象
            layoutInflater = LayoutInflater.from(context);
        }

        //该方法是总共有多少个item
        @Override
        public int getCount() {
            return project.size();
        }

        //该方法是得到每个item的值
        @Override
        public Object getItem(int position) {
            return project.get(position);
        }

        //该方法是得到每个item的id
        @Override
        public long getItemId(int position) {
            return position;
        }

        //该方法是获得视图,也是这些里面最重要的方法
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if (convertView == null) {
                //首先,我们通过布局填充器获得item的布局
                convertView = layoutInflater.inflate(R.layout.item_layout, null);
                //实例化ViewHolder
                viewHolder = new ViewHolder();
                //根据item的布局找到图片
                viewHolder.iv_icon = (ImageView) convertView.findViewById(iv_icon);
                //根据item的布局找到标题
                viewHolder.tv_title = (TextView) convertView.findViewById(tv_title);
                //根据item的布局找到内容
                viewHolder.tv_content = (TextView) convertView.findViewById(tv_content);
                //设置tag标记
                convertView.setTag(viewHolder);
            }
            viewHolder = (ViewHolder) convertView.getTag();
            //设置图片源
            viewHolder.iv_icon.setImageResource(project.get(position).icon);
            //设置标题文字
            viewHolder.tv_title.setText(project.get(position).title);
            //设置内容文字
            viewHolder.tv_content.setText(project.get(position).content);
            //返回item的布局
            return convertView;
        }

        class ViewHolder {
            ImageView iv_icon;
            TextView tv_title;
            TextView tv_content;
        }
    }
}

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

  

这样,我们的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. DataGridView控件判断滚动条是否滚动到当前已加载的数据行底部

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

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

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

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

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

  7. Android 开源控件系列_1

    第一部分 个性化控件(View) 主要介绍那些不错个性化的View,包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.Pro ...

  8. android 基础控件(EditView、SeekBar等)的属性及使用方法

        android提供了大量的UI控件,本文将介绍TextView.ImageView.Button.EditView.ProgressBar.SeekBar.ScrollView.WebView ...

  9. Android基本控件之Menus

    在我们的手机中有很多样式的菜单,比如:我们的短信界面,每条短信,我们长按都会出现一个菜单,还有很多的种类.那么现在,我们就来详细的讨论一下安卓中的菜单 Android的控件中就有这么一个,叫做Menu ...

随机推荐

  1. Windows Azure Storage (17) Azure Storage读取访问地域冗余(Read Access – Geo Redundant Storage, RA-GRS)

    <Windows Azure Platform 系列文章目录> 细心的用户会发现,微软在国外和国内的数据中心建设都是成对的,比如香港数据中心(Asia East)和新加坡的数据中心(Sou ...

  2. 深入了解 JavaScript 中的 for 循环

    在ECMAScript5(简称 ES5)中,有三种 for 循环,分别是: 简单for循环 for-in forEach 在2015年6月份发布的ECMAScript6(简称 ES6)中,新增了一种循 ...

  3. 跟着百度学PHP[4]OOP面对对象编程-10-静态关键字static

    使用static关键字可以将类中的成员标识为静态的,既可以用来标识成员属性,也可以用来标识成员方法. 以Person类为例,如果在person类中有一个“$country=’china’”的成员属性, ...

  4. SpringMVC从Controller跳转到另一个Controller

    1. 需求背景   需求:spring MVC框架controller间跳转,需重定向.有几种情况:不带参数跳转,带参数拼接url形式跳转,带参数不拼接参数跳转,页面也能显示. 本来以为挺简单的一件事 ...

  5. 在eclipse中打开项目所在的目录

    展开如下菜单: Run ---- External Tools ---- External Tools Configurations   在 program 下面新建一个工具 program--右击- ...

  6. JS-JQ实现页面滚动时元素智能定位(顶部-其他部位)

      先看效果:     阅读前提:充分理解div的三种定位方式:浮动,相对定位,绝对定位 方法一(顶部)      原理:直接使用css 进行控制:缺点:不兼容ie6-:      实现:positi ...

  7. 2016年省赛G题, Parenthesis

    Problem G: Parenthesis Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 398  Solved: 75[Submit][Status ...

  8. OpenFlow Switch学习笔记(二)——OpenFlow Ports

    OpenFlow Ports是OpenFlow Switch与剩余网络之间传递Packet的网络接口.OpenFlow Switches之间通过OpenFlow Ports彼此相互逻辑连接.一个Ope ...

  9. java 金额计算,商业计算 double不精确问题 BigDecimal,Double保留两位小数方法

    解决办法================== http://blog.javaxxz.com/?p=763 一提到Java里面的商业计算,我们都知道不能用float和double,因为他们无法 进行精 ...

  10. Python分布式爬虫原理

    转载 permike 原文 Python分布式爬虫原理 首先,我们先来看看,如果是人正常的行为,是如何获取网页内容的. (1)打开浏览器,输入URL,打开源网页 (2)选取我们想要的内容,包括标题,作 ...