上拉加载&下拉刷新
思路详解:
自定义的带有下拉刷新和上拉加载的ListView开始时,跟系统的ListView一样。不过多了个header和footer只不过这两个布局以不同的方式隐藏起来了而已。(header是在手机屏幕外的上面,footer是直接隐藏起来了。因为下拉和上拉加载不同,下拉加载有手势判断,要出现动画效果,根据下拉的各种手势,来设置具体的操作。如果直接跟footer一样首先默认header的View.setVisibility(GONE),当有下拉手势时再设置View.setVisibility(View.VISIBLE)就会没有良好的动画效果。)
如下图所示:
上拉加载:
上拉加载适用于需要加载的数据量很大时,如果一下子加载完。会使ListView出现卡顿。这时候,如果利用上拉加载。先加载一部分数据。当上拉时,再加载其他的一部分数据。这样就会有很好的用户体验。
接下来看上拉加载逻辑:上拉加载我们利用的是AbsListView.OnScrollListener这个接口。 它有两个方法需要重写:
1.publicvoid onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount) {};
//滚动时一直回调,直到停止滚动时才停止回调。单击时回调一次。
//firstVisibleItem:当前能看见的第一个列表项ID(从0开始)
//visibleItemCount:当前能看见的列表项个数(小半个也算)
//totalItemCount:列表项共数
2.publicvoid onScrollStateChanged(AbsListView view,int scrollState) {};
//正在滚动时回调,回调2-3次,手指没抛则回调2次。scrollState = 2的这次不回调
//回调顺序如下
//第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动
//第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下)
//第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滚动
//当屏幕停止滚动时为0;当屏幕滚动且用户使用的触碰或手指还在屏幕上时为1;
//由于用户的操作,屏幕产生惯性滑动时为2
//当滚到最后一行且停止滚动时,执行加载
自定义LoadListView
1.定义变量
private int lastVisibleItem; //最后一个可见项
private int totalItems; //总的item
private View footer; //底部View+头部View;
private boolean isLoading = false;//是否正在加载
private ILoadListener iListener;//自定义的一个加载接口。暴露给MainActivity让它实现具体加载操作。可以根据需求不同而改写。
2.加载布局文件及设置监听
private void initViews(Context context) {
//获得footer+header布局文件
LayoutInflater inflater =LayoutInflater.from(context);
footer = inflater.inflate(R.layout.footer,null);
footer.findViewById(R.id.ll_footer).setVisibility(GONE);//初始化时设置footer不可见
this.addFooterView(footer);
this.setOnScrollListener(this);//设置滚动监
}
3.重写OnScrollListener的两个方法
4.完成接口设置
/**
* 加载更多数据的回调接口
*/
public interface ILoadListener {
public void onLoad();
}
//上拉加载完毕
public void loadCompleted(){
isLoading =false;
footer.findViewById(R.id.ll_footer).setVisibility(GONE);
}
public void setInterface(ILoadListener iListener){
this.iListener=iListener;
}
5.在MainActivity中实现LoadListView暴露的接口中的onLoad方法
//实现onLoad()方法。
@Override
public void onLoad() {
//添加延时效果模拟数据加载
Handler handler= new Handler() ;
handler.postDelayed(new Runnable() {
@Override
public void run() {
initNewDatas();//得到新数据
arrayAdapter.notifyDataSetChanged();//刷新ListView;
mListView.loadCompleted();
}
}, 2000);
}
上拉加载数据完成。
下拉刷新:
1.header布局文件
2.在LoadListView中定义变量
private boolean isRemark = false;//判断是否在当前页的最顶端并下滑
private int startY; //Y坐标 记录手指开始按下的坐标
private RLoadListener rLoadListener;//自定义的一个加载接口。暴露给MainActivity让它实现具体加载操作。可以根据需求不同而改写。
private int scrollState;//当前滚动的 状态
private int headerHeight;//顶部布局文件的高度
final int NONE= 0;//正常状态
final int PULL =1;//下拉
final int RELESE =2;//释放
final int REFLASHING =3; //刷新
private int state=0;//判断当前状态,默认为正常状态
private int firstVisibleItem;//第一个可见项
private View header; //头部View;
3.添加头部到ListView中
这里添加头布局的时候需要计算header到底要移出屏幕多少的距离(移出的距离即为header的高),并且要告知父布局
4.在OnScrollListener的两个方法中记录一些状态量便于后面对OnTouch事件的操作
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState =scrollState;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int
totalItemCount) {
this.firstVisibleItem = firstVisibleItem;
}
5.利用OnTouchEvent对下拉手势操作进行监听:
先来说明下拉刷新会出现的情况:
1.下拉距离过短,不进行数据刷新。
2.下拉到一定距离刷新数据。
当进行下拉刷新的时候,手势有3种状态:
1.刚按下的时候;
2.手指下滑移动的时候;
3.手指抬起释放的时候。
这3种手指状态又对应了header不同的View状态,然后根据view的状态,再来改变header的显示。
6.下拉刷新接口,暴露给MainActivity来具体实现到底刷新出什么数据:
/**
* 下拉刷新接口
*/
public interface RLoadListener{
public void onRefresh();
}
public void setReflashInterface(RLoadListener rLoadListener){
this.rLoadListener =rLoadListener;
}
7.刷新完要做的工作
/**
* 获取完整数据
*
*/
public void reflashComplete(){
state = NONE;
isRemark = false;
reflashViewByState();
TextView lasetupdate_time = (TextView) header.findViewById(R.id.tv_lastupdate_time);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date date = new Date(System.currentTimeMillis());
String time = simpleDateFormat.format(date);
lasetupdate_time.setText(time);
}
8.在MainActivity中应用LoadListView
SwipeRefreshLayout实现下拉刷新(仅效果)
- isRefreshing()
判断当前的状态是否是刷新状态。
- setColorSchemeResources(int... colorResIds)
设置下拉进度条的颜色主题,参数为可变参数,并且是资源id,可以设置多种不同的颜色,每转一圈就显示一种颜色。
- setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener)
设置监听,需要重写onRefresh()方法,顶部下拉时会调用这个方法,在里面实现请求数据的逻辑,设置下拉进度条消失等等。
- setProgressBackgroundColorSchemeResource(int colorRes)
设置下拉进度条的背景颜色,默认白色。
- setRefreshing(boolean refreshing)
设置刷新状态,true表示正在刷新,false表示取消刷新。
使用:
1.设置布局
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
2.在代码中使用
在该布局文件对应的Activity或其他类中获取布局id,先设置ListView显示的适配器,然后再设置SwipeRefreshLayout。
注:不能在onCreate中设置,这个表示当前是刷新状态,如果一进来就是刷新状态,SwipeRefreshLayout会屏蔽掉下拉事件 //swipeRefreshLayout.setRefreshing(true);
//声明控件
private SwipeRefreshLayout swiper;
private ListView mListView;
//声明并初始化ListView的数据源
private List<String> list = new ArrayList<>();
//声明ListView的适配器
private ArrayAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.swipe_layout);
//初始化SwipeRefreshLayout
swiper = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
//为SwipeRefreshLayout设置监听事件
swiper.setOnRefreshListener(this);
//为SwipeRefreshLayout设置刷新时的颜色变化,最多可以设置4种
swiper.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
//初始化ListView
mListView = (ListView) findViewById(R.id.list_view);
//初始化适配器
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, getData());
//设置适配器
mListView.setAdapter(adapter);
}
//增加数据
private List<String> getData() {
list.add("testdata1");
list.add("testdata2");
list.add("testdata3");
list.add("testdata4");
return list;
}
//实现OnRefreshListener 的onRefresh()方法
@Override
public void onRefresh() {
//刷新
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//结束后停止刷新
swiper.setRefreshing(false);
}
}, 3000);
}
//简单示例,手动添加数据
private void refreshData() {
list.add(0, String.valueOf((int) (Math.random() * 10)));
adapter.notifyDataSetChanged();
}