android中下拉刷新是一个很常见的功能,今天我们就来介绍一个能很方便的实现下拉刷新的方式。
PullToRefresh是github上的一个非常好用的下拉刷新的开源库,下面是PullToRefresh的官方下载地址:
https://github.com/chrisbanes/Android-PullToRefresh
下面正式开始介绍PullToRefresh在项目中的应用,开发工具使用的是android studio。
1、下载PullToRefresh
打开上面的PullToRefresh官网链接,点击绿色按钮“Clone or download",然后点击Download ZIP,下载PullToRefresh压缩包。
2、将PullToRefresh配置到项目中
首先新建一个android项目,然后找到刚才下载的PullToRefresh压缩包,解压后得到目录如下:
然后将目录中的library作为一个module导入项目,具体步骤哦:点击android studio菜单栏的File->New->Import Module,弹出New Module窗口,在Source directory出填写library的完整路径,也可以点击右边的浏览按钮,找到library,选中点击OK,如下图:
导入的module名字默认为文件夹的名字library,我们可以自己给这个module起一个名字,点击next->finish完成。
导入完成后项目目录如下:
到这里还没完成,如果想正常使用PullToRefresh,我们还需要将app和PullToRefresh这两个module进行关联。点击菜单栏的File->Project Structure,弹出窗口如下,点击左侧的app,然后点击上方的Dependencies,点击右边的绿色加号,选择第三个选项
点击上图中的第三个选项之后弹出module选择窗口,这里我们除了app之外只有一个module,就是我们刚才添加的library(我自己起的名字是pulltorefresh-library),选中这个module,点击OK
这时我们能看到app下多了一个依赖,就是我们刚才添加的pulltorefresh-library。到这里,我们就可以在项目中正常使用PullToRefresh了。
另外,值得注意的一点是,在PullToRefresh的res目录下有很多的"values-xx"目录,如下图所示。
打开看一下会发现里卖弄是定义的一些字符串,其实这些文件是为了翻译values目录下定义的字符串的,是PullToRefresh为了提供更好的国际兼容性而做的。个人建议使用时将这些"values-xx"全部删除,然后打开values目录下的字符创定义文件,将其中定义的字符串的值修改为固定值,如下图所示。如果不这样做的话,在打包apk时会提示在其他地方定义的字符串在某个字符串定义文件中缺少翻译而打包失败。
3、在项目中使用PullToRefresh实现下拉刷新
通过查看PullToRefresh的类库可知,PullToRefresh支持ExpandableListView、GridView、HorizontalScrollView、ListView、ScrollView、WebView等控件的下拉刷新和上拉加载,这篇文章将以其中的ListView(扩展类为PullToRefreshListView)为例进行说明。
1)首先,打开我们刚才新建的android项目,找到layout目录下的activity_main.xml布局文件,在里面添加一个PullToRefreshListView控件,代码如下。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.conan.pulltorefreshdemo.MainActivity">
<com.handmark.pulltorefresh.library.PullToRefreshListView
android:id="@+id/lv_mylistview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:ptrAnimationStyle="flip"
app:ptrHeaderBackground="#f0f0f0"
app:ptrHeaderTextColor="#000000">
</com.handmark.pulltorefresh.library.PullToRefreshListView>
</RelativeLayout>
需要注意有两点,第一,因为使用了自定义的控件,所以需要在PullToRefreshListView控件或其父控件中添加命名空间xmlns:app="http://schemas.android.com/apk/res-auto";第二,PullToRefreshListView中的属性ptrAnimationStyle、ptrHeaderBackground和ptrHeaderTextColor为PullToRefresh中自定义的属性,其中ptrAnimationStyle用来设置刷新和加载时的动画效果,ptrHeaderBackground用来设置刷新和加载区域的背景,ptrHeaderTextColor用来设置刷新和加载区域的字体颜色,PullToRefresh还有一些其他的属性,具体可以查看PullToRefresh类库的values目录下的attrs.xml文件。
2)有了一个ListView,我们还需要一个item布局文件,用来填充这个ListView。
在layout目录下再新建一个my_list_item.xml布局文件,代码如下。
my_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp">
<ImageView
android:id="@+id/iv_item_pic"
android:layout_width="200dp"
android:layout_height="160dp" />
<TextView
android:id="@+id/tv_item_name"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_toRightOf="@id/iv_item_pic"
android:layout_marginLeft="10dp"
android:layout_alignParentTop="true"
android:gravity="center_vertical"
android:textSize="24sp"/>
<TextView
android:id="@+id/tv_item_introduce"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_toRightOf="@id/iv_item_pic"
android:layout_marginLeft="10dp"
android:layout_below="@id/tv_item_name"
android:gravity="center_vertical"
android:textSize="18sp"/>
<Button
android:id="@+id/btn1_item"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_below="@id/tv_item_introduce"
android:layout_toRightOf="@id/iv_item_pic"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:gravity="center"
android:textSize="20sp"
android:text="操作1"
android:background="@drawable/btn_selector"/>
<Button
android:id="@+id/btn2_item"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginLeft="20dp"
android:layout_below="@id/tv_item_introduce"
android:layout_toRightOf="@id/btn1_item"
android:layout_marginTop="10dp"
android:gravity="center"
android:textSize="20sp"
android:text="操作2"
android:background="@drawable/btn_selector"/>
</RelativeLayout>
</RelativeLayout>
在这个布局文件中,我们用一个ImageView来显示item的图片,用两个TextView来显示对item的描述信息,用两个Button来模拟对item的操作。
3)布局文件准备完成后,就可以在MainActivity中对ListView进行初始化了。
package com.example.conan.pulltorefreshdemo;
import android.content.Context;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.handmark.pulltorefresh.library.ILoadingLayout;
import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshListView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
private PullToRefreshListView myListView;
private List<Map<String,Object>> dataList;
private ListViewAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myListView = (PullToRefreshListView) findViewById(R.id.lv_mylistview);
/**
* PullToRefreshListView的setMode方法有三个可选参数:
* Mode.BOTH、Mode.PULL_FROM_END、Mode.PULL_FROM_START
* Mode.BOTH:同时支持下拉刷新和上拉加载,需要设置刷新Listener为OnRefreshListener2,
* 并实现onPullDownToRefresh()方法和onPullUpToRefresh()方法
* Mode.PULL_FROM_START仅支持下拉刷新,Mode.PULL_FROM_END仅支持上拉加载,这两种模式需要设置
* Listener为OnRefreshListener,并实现onRefresh()方法;也可以设置刷新Listener
* 为OnRefreshListener2,并实现onPullDownToRefresh()方法和onPullUpToRefresh()方法,
* 但是Mode.PULL_FROM_START只调用onPullDownToRefresh()方法,Mode.PULL_FROM_END只调
* 用onPullUpToRefresh()方法
*/
myListView.setMode(PullToRefreshBase.Mode.BOTH);
//设置下拉刷新时显示的文本
ILoadingLayout headerLayout = myListView.getLoadingLayoutProxy(true, false);
headerLayout.setPullLabel("下拉可以刷新");
headerLayout.setRefreshingLabel("玩儿命刷新中...");
headerLayout.setReleaseLabel("释放立即刷新");
//设置上拉加载时显示的文本
ILoadingLayout tailerLayout = myListView.getLoadingLayoutProxy(false, true);
tailerLayout.setPullLabel("上拉可以加载");
tailerLayout.setRefreshingLabel("玩儿命加载中...");
tailerLayout.setReleaseLabel("释放立即加载");
//设置刷新监听器
myListView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2<ListView>() {
//下拉时执行的方法
@Override
public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) {
//模拟网络请求数据
new LoadDataAsyncTask(MainActivity.this).execute();
}
//上拉时执行的方法
@Override
public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
//模拟网络请求数据
new LoadDataAsyncTask(MainActivity.this).execute();
}
});
//初始化数据dataList
getData();
//实例化自定义的适配器ListViewAdapter,将dataList中的数据设置到my_list_item布局的控件中
adapter = new ListViewAdapter(this, R.layout.my_list_item, dataList);
myListView.setAdapter(adapter);
}
public void getData() {
dataList = new ArrayList<>();
for (int i = 0;i < 10;i ++) {
Map<String,Object> map = new HashMap<>();
map.put("itemPic",R.drawable.error_img);
map.put("name","Item" + i);
map.put("introduce","introduce of item " + i);
dataList.add(map);
}
}
/**
* 自定义适配器类ListViewAdapter,继承BaseAdapter,实例化时会先调用getCount()方法确定有多少个item,
* 然后调用getView()方法一遍一遍的进行数据的渲染
*/
private class ListViewAdapter extends BaseAdapter {
private Context context;
private List<Map<String,Object>> dataList;
private int itemLayout;
public ListViewAdapter (Context context, int itemLayout, List<Map<String,Object>> dataList) {
super();
this.context = context;
this.dataList = dataList;
this.itemLayout = itemLayout;
}
@Override
public int getCount() {
if (dataList != null && dataList.size() != 0) {
return dataList.size();
} else {
return 0;
}
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
final Map<String, Object> itemData = dataList.get(position);
if (convertView == null) {
holder = new ViewHolder();
convertView = View.inflate(context, itemLayout, null);
holder.itemPic = convertView.findViewById(R.id.iv_item_pic);
holder.name = convertView.findViewById(R.id.tv_item_name);
holder.introduce = convertView.findViewById(R.id.tv_item_introduce);
holder.btn1 = convertView.findViewById(R.id.btn1_item);
holder.btn2 = convertView.findViewById(R.id.btn2_item);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.itemPic.setBackgroundResource((Integer) itemData.get("itemPic"));
holder.name.setText((String) itemData.get("name"));
holder.introduce.setText((String) itemData.get("introduce"));
holder.btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(context, "对" + itemData.get("name") + "进行操作1",Toast.LENGTH_SHORT).show();
}
});
holder.btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(context, "对" + itemData.get("name") + "进行操作2",Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public Object getItem(int position) {
return dataList.get(position);
}
}
private static class ViewHolder {
ImageView itemPic;
TextView name;
TextView introduce;
Button btn1;
Button btn2;
}
/**
* 模拟进行网络数据请求
*/
private class LoadDataAsyncTask extends AsyncTask<Void, Void, String> {
private MainActivity activity;
public LoadDataAsyncTask(MainActivity activity) {
this.activity = activity;
}
@Override
protected String doInBackground(Void... params) {
try {
activity.getData();
Thread.sleep(1000);
return "SUCCESS";
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (s.equals("SUCCESS")) {
adapter.notifyDataSetChanged();
myListView.onRefreshComplete();
}
}
}
}