> For the complete documentation index, see [llms.txt](https://xxgqin.gitbook.io/android/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://xxgqin.gitbook.io/android/appendix/app01-2.md).

# 第十六个项目——使用SwipeRefreshLayout实现下拉刷新

## 实验目的

* 掌握**SwipeRefreshLayout**布局控件使用方式；

## 实验要求

* 掌握**SwipeRefreshLayout**进行刷新加载数据的方法；

## 实验内容

在[第七个项目](https://xxgqin.gitbook.io/android/ch02/ch02-4)中**ListView**中的数据在**MainActivity**初始化时从**arrays.xml**文件中进行读取，其数量是固定的。实际项目开发中，通常需要通过下拉页面进行数据的刷新加载。本实验中通过使用**SwipeRefreshLayout**实现页面的下拉刷新，为了简便起见，当用户下拉刷新App页面时，从**arrays.xml**随机生成一条新闻加入**ListView**的顶部，项目的最终运行效果如[图1 SwipeRefreshLayout实现下拉刷新](/android/appendix/app01-2.md#slf_running_screenshot)所示。

![图1-a. 下拉刷新](http://www.funnycode.net/guet/img/app01/sfl_running_screenshot1.jpg)

![图1-b. 释放下拉](http://www.funnycode.net/guet/img/app01/sfl_running_screenshot2.jpg)

![图1-c. 再次下拉刷新](http://www.funnycode.net/guet/img/app01/sfl_running_screenshot3.jpg)

![图1-d. 释放下拉](http://www.funnycode.net/guet/img/app01/sfl_running_screenshot4.jpg)

### 步骤一，打开第七个项目或第十五个项目

打开[第七个项目](https://xxgqin.gitbook.io/android/ch02/ch02-4)或[第十五个项目](https://xxgqin.gitbook.io/android/appendix/app01-1)，在该项目基础上完成本次实验。

### 步骤二，修改activity\_main.xml布局文件

打开**activity\_main.xml**文件，需要加入**SwipeRefreshLayout**控件作为**ListView**控件的父容器，具体代码如下所示。

```markup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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"
    tools:context=".MainActivity">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_margin="8dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">
        <ListView
            android:id="@+id/lv_news_list"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:scrollbars="none"
            android:divider="@android:color/transparent"
            android:dividerHeight="8dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            />
    </android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>
```

### 步骤二，修改MainActivity.java文件

**1. 在MainActivity类中定义SwipeRefreshLayout控件对象**

在**MainActivity**类中定义私有**SwipeRefreshLayout**控件对象，并在**onCreate()**&#x65B9;法中进行控件对象绑定操作。

**2. 设置SwipeRefreshLayout控件下拉刷新事件侦听器**

响应**SwipeRefreshLayout**控件的下拉刷新需要设置该控件的**OnRefreshListener**事件侦听器，在**onRefresh()**&#x65B9;法中调用了**MainActivity**类定义的私有方法，用于刷新数据。

上述两步的代码如下所示。

```java
public class MainActivity extends AppCompatActivity {
    ...
    private List<News> newsList = new ArrayList<>();
    private NewsAdapter newsAdapter = null;
    private ListView lvNewsList;

    private SwipeRefreshLayout swipe;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initData();

        NewsAdapter newsAdapter = new NewsAdapter(MainActivity.this,
                R.layout.list_item, newsList);

        ListView lvNewsList = findViewById(R.id.lv_news_list);
        lvNewsList.setAdapter(newsAdapter);

        swipe = findViewById(R.id.swipe);  

        ...

        swipe.setOnRefreshListener(
            new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refreshData();
            }
        });
    }
    ...
}
```

**3. 定义refreshData()私有方法刷新数据**

刷新数据主要通过实例化一个**News**新闻对象，该对象数据仍然从**arrays.xml**数组资源文件中得到，但是采用了**Random**对象随机产生范围从 0\~19 的下标，从而模拟生成新的新闻对象。

通过**NewsAdapter.insert(News object, int index)**&#x65B9;法将随机生成的**News**新闻对象插入数据源的头部（index为0）。当**NewsAdapter**适配器中的数据发生变化时，需要调用**NewsAdapter.notifyDataSetChanged()**&#x65B9;法通知与适配器绑定的**ListView**列表UI控件刷新页面。

最后调用**SwipeRefreshLayout.setRefreshing(false)**&#x65B9;法，取消刷新动作并隐藏**SwipeRefreshLayout**控件。

```java
public class MainActivity extends AppCompatActivity {
    ...
    private void refreshData() {
        Random random = new Random();
        int index = random.nextInt(19);

        News news = new News();

        news.setTitle(titles[index]);
        news.setAuthor(authors[index]);
        news.setContent(contents[index]);
        news.setImageId(images.getResourceId(index, -1));

        newsAdapter.insert(news, 0);
        newsAdapter.notifyDataSetChanged();
        swipe.setRefreshing(false);
    }
}
```

### 步骤三，编译项目并部署APK运行

完成上述步骤后，编译项目并将其部署至虚拟机或物理机上，App运行后，在新闻列表页中下拉刷新观察新闻列表是否响应刷新动作在列表顶部新增一条新闻，App运行结果应与[图1 SwipeRefreshLayout实现下拉刷新](/android/appendix/app01-2.md#slf_running_screenshot)相同。

## 实验小结

通过本次实验，你应该掌握了如下知识内容：

* 使用**SwipeRefreshLayout**响应下拉刷新；
* 使用**Adapter.notifyDataSetChanged()**&#x7B49;方法进行数据变更；


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xxgqin.gitbook.io/android/appendix/app01-2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
