自定义View——利用下拉刷新组件实现上拉加载
2016-04-21 13:47
926 查看
注:本文demo已经提交github,地址完整代码如下,demo工程已经上传至GitHub,
github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout
感谢大家支持!
在Android开发中,我们经常会用到列表下拉刷新和上拉加载的功能。
Google在support.v4包中提供了一个组件可以用来进行下来刷新,这个组件是SwipeRefreshLayout。
下面我们来看一下这个组件的使用:
在布局文件中加上xml代码
在MainActivity.java代码中添加代码:
在Activity的onCreate方法中初始化实例
接下来给SwipeRefreshLayout添加OnRefreshListener监听
运行效果如图所示
![](https://img-blog.csdn.net/20160421130408245)
但是在日常开发中,我们不仅需要下拉刷新的功能,还应该有上拉继续加载的功能,但是Google并没有给我们提供上拉加载的方法,那怎么办呢?我们可以自定义一个View,自己添加上拉加载方法。
我们模仿Swiperefreshlayout的使用下拉刷新的方法,去设计上拉加载的方法。添加了上拉加载的代码形式大约为
所以,我们在重写下拉刷新组件前先写一个接口OnLoadListener
我们写一个UpLoadSwipeRefreshLayout类,继承SwipeRefershLayout类。我们应该在什么时候开始上拉加载呢?当然是在子ListView滑动到最底部的时候,所以,我们可以开始进行上拉加载的时候应该满足一下条件:
① 判断listview已经滑动到了最底部
② 判断手指是在向上滑动
③ 当前上拉加载不正在进行
编写类
代码如下:
因为ListView是此组件的子view,那么我们应该在开始的时候去检测,子view是否是listview。
重写onLayout()方法,获取子类
构造一个getListView方法
重写事件拦截事件,防止滑动事件发生冲突
为了获取子listview的滑动情况,使用我们应该让组件实现OnScrollListener接口。
即
编写isCanLoad()方法,这里就用到了上方我们需要满足的三个条件
依次编写2个方法
这时候我们还需要编写loadData()方法
编写设置可加载的方法setLoading()
对了,还没添加设置上拉加载监听的方法!
编写setOnUpLoadListener()方法
这个类我对外提供了一个变量,代表listview的底视图的资源id
可以通过setFooterResource方法进行添加并实例化此底部view
编写此方法
这时候这个类我们就编写好了,完整的代码我会在博客的结尾放出。
我们先来看看这段代码的使用
将刚才的布局文件修改成如下:
添加尾视图的资源id文件,layout_up_load_view.xml
修改MainActivity.java的代码
运行效果如图:
![](https://img-blog.csdn.net/20160421134553410)
完整代码如下,demo工程已经上传至GitHub
github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout
github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout
感谢大家支持!
在Android开发中,我们经常会用到列表下拉刷新和上拉加载的功能。
Google在support.v4包中提供了一个组件可以用来进行下来刷新,这个组件是SwipeRefreshLayout。
下面我们来看一下这个组件的使用:
在布局文件中加上xml代码
<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_refresh_layout" 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>
在MainActivity.java代码中添加代码:
/** * 添加变量 * / private SwipeRefreshLayout swipeRefreshLayout; private ListView listView; private ArrayList<String> list = new ArrayList<>(); private ArrayAdapter<String> adapter;
在Activity的onCreate方法中初始化实例
/** * 实例化变量,此处省略initList()方法 * 此方法内容为添加了N个字符串"item" */ initList(); swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout); listView = (ListView) findViewById(R.id.list_view); adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list); listView.setAdapter(adapter);
接下来给SwipeRefreshLayout添加OnRefreshListener监听
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { Toast.makeText(MainActivity.this, "is refreshing!", Toast.LENGTH_SHORT).show(); } });
运行效果如图所示
但是在日常开发中,我们不仅需要下拉刷新的功能,还应该有上拉继续加载的功能,但是Google并没有给我们提供上拉加载的方法,那怎么办呢?我们可以自定义一个View,自己添加上拉加载方法。
我们模仿Swiperefreshlayout的使用下拉刷新的方法,去设计上拉加载的方法。添加了上拉加载的代码形式大约为
view.setOnLoadListener(new OnLoadListener() { @Override public void onLoad() { //在此执行上拉加载的逻辑 view.setloading(false); //逻辑执行完毕后停止上拉加载 } });
所以,我们在重写下拉刷新组件前先写一个接口OnLoadListener
public interface OnLoadListener { public void onLoad(); //需要用户重写的方法 }
我们写一个UpLoadSwipeRefreshLayout类,继承SwipeRefershLayout类。我们应该在什么时候开始上拉加载呢?当然是在子ListView滑动到最底部的时候,所以,我们可以开始进行上拉加载的时候应该满足一下条件:
① 判断listview已经滑动到了最底部
② 判断手指是在向上滑动
③ 当前上拉加载不正在进行
编写类
代码如下:
public class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout { private ListView mListView; //子view private boolean isCanLoading = false; //是否正可以加载,默认不可用 private OnLoadListener mOnLoadListener; private View mFooterView; //上拉加载的视图 private int firstY; private int lastY; private int mTouchSlop; //最短的滑动距离 private int footerResource; //加载视图的资源ID public UpLoadSwipeRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); //获取系统默认的最短滑动距离 } }
因为ListView是此组件的子view,那么我们应该在开始的时候去检测,子view是否是listview。
重写onLayout()方法,获取子类
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mListView == null) { getListView(); //获取子view } }
构造一个getListView方法
private void getListView() { int childs = getChildCount(); if (childs > 0) { View childView = getChildAt(0); //获取第一个子view if (childView instanceof ListView) { /** * 如果子view的类型是ListView */ mListView = (ListView) childView; //给子listview设置滑动监听 mListView.setOnScrollListener(this); } } }
重写事件拦截事件,防止滑动事件发生冲突
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: /** * 获得触摸事件刚刚开始的时候,手指触摸的坐标 */ firstY = (int) ev.getRawY(); break; case MotionEvent.ACTION_MOVE: /** * 获得触摸事件结束的时候,手指离开时候的坐标 */ lastY = (int) ev.getRawY(); break; case MotionEvent.ACTION_UP: if (isCanLoad()) { loadData(); } break; default: break; } return super.onInterceptTouchEvent(ev); }
为了获取子listview的滑动情况,使用我们应该让组件实现OnScrollListener接口。
即
class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout implements AbsListView.OnScrollListener { @Override public void onScrollStateChanged(AbsListView view, int scrollState) {} /** * 重写滑动时间 * / @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (isCanLoad()) { /** * 滑动过程中如果可以加载数据 * 就执行加载数据的函数 */ loadData(); } } }
编写isCanLoad()方法,这里就用到了上方我们需要满足的三个条件
private boolean isCanLoad() { return isBottom() && isPushUp() && !isCanLoading; }
依次编写2个方法
/** * 判断listview是否到了底部 */ private boolean isBottom() { if (mListView != null && mListView.getAdapter() != null) { /** * 当前可加的最后一项就是listview的末尾项 * 说明滑动到了最底部 */ return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1); } return false; }
/** * 判断是否正在上拉 */ private boolean isPushUp() { /** * 往上滑动的距离大于系统默认的滑动距离 */ return firstY - lastY >= mTouchSlop; }
这时候我们还需要编写loadData()方法
/** * 加载数据 */ private void loadData() { if (mOnLoadListener != null) { setLoading(true); /** * 处理加载的逻辑 * 具体逻辑在回调中完成 */ mOnLoadListener.onLoad(); } }
编写设置可加载的方法setLoading()
public void setLoading(boolean isCanLoad) { this.isCanLoading = isCanLoad; if (isCanLoad == true) { /** * 加载,添加底部的正在加载视图 */ mListView.addFooterView(mFooterView); } else { /** * 不在加载中,将底部的加载中视图移除 */ mListView.removeFooterView(mFooterView); firstY = 0; lastY = 0; } }
对了,还没添加设置上拉加载监听的方法!
编写setOnUpLoadListener()方法
public void setOnLoadListener(OnLoadListener onLoadListener) { this.mOnLoadListener = onLoadListener; }
这个类我对外提供了一个变量,代表listview的底视图的资源id
可以通过setFooterResource方法进行添加并实例化此底部view
编写此方法
public void setFooterResource(int footerResource) { this.footerResource = footerResource; /** * 实例化布局view */ this.mFooterView = LayoutInflater.from(getContext()).inflate(footerResource, null, false); }
这时候这个类我们就编写好了,完整的代码我会在博客的结尾放出。
我们先来看看这段代码的使用
将刚才的布局文件修改成如下:
<com.example.chenglei.myapplication.UpLoadSwipeRefreshLayout android:id="@+id/swipe_refresh_layout" 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> </com.example.chenglei.myapplication.UpLoadSwipeRefreshLayout>
添加尾视图的资源id文件,layout_up_load_view.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="80dp" android:background="@android:color/white"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:orientation="horizontal"> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="5dp" android:indeterminateDuration="1500" /> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center" android:layout_margin="5dp" android:gravity="center" android:text="正在加载中..." /> </LinearLayout> </RelativeLayout>
修改MainActivity.java的代码
swipeRefreshLayout.setFooterResource(R.layout.layout_up_load_view); //添加正在加载中的视图 swipeRefreshLayout.setOnLoadListener(new OnLoadListener() { @Override public void onLoad() { //上拉加载的操作 } });
运行效果如图:
完整代码如下,demo工程已经上传至GitHub
github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout
package com.example.chenglei.myapplication;
import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.ListView;
/**
* Created by chenglei on 2016/4/20.
* 重写下拉刷新组件,添加上拉加载的功能
*/
public class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout implements AbsListView.OnScrollListener {
private ListView mListView; //子view
private boolean isCanLoading = false; //是否正可以加载,默认不可用
private OnLoadListener mOnLoadListener;
private View mFooterView; //上拉加载的视图
private int firstY;
private int lastY;
private int mTouchSlop; //最短的滑动距离
private int footerResource;
public UpLoadSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); //获取系统默认的最短滑动距离
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mListView == null) {
getListView();
}
}
/**
* 获取子view
*/
private void getListView() { int childs = getChildCount(); if (childs > 0) { View childView = getChildAt(0); //获取第一个子view if (childView instanceof ListView) { /** * 如果子view的类型是ListView */ mListView = (ListView) childView; //给子listview设置滑动监听 mListView.setOnScrollListener(this); } } }
/**
* 触摸事件拦截
*/
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: /** * 获得触摸事件刚刚开始的时候,手指触摸的坐标 */ firstY = (int) ev.getRawY(); break; case MotionEvent.ACTION_MOVE: /** * 获得触摸事件结束的时候,手指离开时候的坐标 */ lastY = (int) ev.getRawY(); break; case MotionEvent.ACTION_UP: if (isCanLoad()) { loadData(); } break; default: break; } return super.onInterceptTouchEvent(ev); }
/**
* 判断是否可以进行加载
*/
private boolean isCanLoad() { return isBottom() && isPushUp() && !isCanLoading; }
/** * 判断listview是否到了底部 */ private boolean isBottom() { if (mListView != null && mListView.getAdapter() != null) { /** * 当前可加的最后一项就是listview的末尾项 * 说明滑动到了最底部 */ return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1); } return false; }
/** * 判断是否正在上拉 */ private boolean isPushUp() { /** * 往上滑动的距离大于系统默认的滑动距离 */ return firstY - lastY >= mTouchSlop; }
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (isCanLoad()) {
/**
* 滑动过程中如果可以加载数据
* 就执行加载数据的函数
*/
loadData();
}
}
/** * 加载数据 */ private void loadData() { if (mOnLoadListener != null) { setLoading(true); /** * 处理加载的逻辑 * 具体逻辑在回调中完成 */ mOnLoadListener.onLoad(); } }
/**
* 设置可加载
*/
public void setLoading(boolean isCanLoad) { this.isCanLoading = isCanLoad; if (isCanLoad == true) { /** * 加载,添加底部的正在加载视图 */ mListView.addFooterView(mFooterView); } else { /** * 不在加载中,将底部的加载中视图移除 */ mListView.removeFooterView(mFooterView); firstY = 0; lastY = 0; } }
/**
* 设置上拉加载监听
*/
public void setOnLoadListener(OnLoadListener onLoadListener) { this.mOnLoadListener = onLoadListener; }
/**
* 设置上拉加载的布局
*/
public void setFooterResource(int footerResource) { this.footerResource = footerResource; /** * 实例化布局view */ this.mFooterView = LayoutInflater.from(getContext()).inflate(footerResource, null, false); }
}
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories