为自己记------android中listview下拉刷新和下拉加载的原理及简单实现
2015-07-06 11:50
791 查看
自定义listview,实现下拉刷新功能和上拉加载功能
1.下拉刷新
实现原理:通过onTouchEvent判断手势,来改变listview的header。
header的状态共4种,自己定义为:none, normal, willrefresh, refreshing ,header在四种状态切换时不仅改变内部组件,同时改变自身的大小。
footer类似
实现:
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.windlu.anywhere.R;
/**
* 自定义的listview,实现上拉刷新和下拉加载
* @author lufeng
*
*/
public class PushAndDownListview extends ListView implements OnScrollListener{
private StateBar headView;//头部view
private StateBar footerView;//脚部view
private int headerContentHeight;//头部view的高度
private int footerContentHeight;//脚部view的高度
private static final int Refresh = 0;//刷新
private static final int Load = 1;//加载
private int Operating=-1;//操作
private PushAndDownListviewRefreshOrLoad mPushAndDownListviewRefreshOrLoad;//加载或刷新的回调接口
public PushAndDownListview(Context context) {
this(context,null);
}
public PushAndDownListview(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context){
//头部
headView = new StateBar(context);
headView.setStatusStrings(getResources().getString(R.string.pull_to_refresh),
getResources().getString(R.string.release_to_refresh),
getResources().getString(R.string.refreshing));
//测量view的高度
measureView(headView);
headerContentHeight=headView.getMeasuredHeight();
//设置view的toppadding值
headtopPadding(-headerContentHeight);
//添加view
addHeaderView(headView);
//脚部
footerView = new StateBar(context);
footerView.setStatusStrings(getResources().getString(R.string.pull_up_load),
getResources().getString(R.string.release_to_load),
getResources().getString(R.string.loading));
measureView(footerView);
footerContentHeight = footerView.getMeasuredHeight();
footerTopPadding(-footerContentHeight);
addFooterView(footerView);
}
float startY;
boolean isRecordedStartY;//是否已经记录StartY
int firstVisibleItemPosition ;
int LastVisibleItemPosition;
int totalItemCounts;
private boolean isRefreshing;
//主要在该方法中实现下拉刷新效果和上拉加载效果
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if((firstVisibleItemPosition==0 || LastVisibleItemPosition==totalItemCounts) && Operating == -1&&!isRefreshing){
startY = ev.getY();
isRecordedStartY=true;
return true;
}
break;
case MotionEvent.ACTION_UP:
isRecordedStartY=false;
if((firstVisibleItemPosition==0 || LastVisibleItemPosition==totalItemCounts) && Operating != -1&&!isRefreshing){
if(Operating == Refresh){
if(headView.getStateType()==StateType.willrefresh){
headView.setRefreshStatus(StateType.refreshing);
headtopPadding(0);
doRefreshWork();
}else if(headView.getStateType()==StateType.refreshing){
}else{
RefreshWorkComplete();
//
Operating=-1;
//
headView.setRefreshStatus(StateType.none);
//
headtopPadding(-headerContentHeight);
}
}else if(Operating == Load){
if(footerView.getStateType()==StateType.willrefresh){
footerView.setRefreshStatus(StateType.refreshing);
footerTopPadding(0);
doLoadWork();
}else if(footerView.getStateType()==StateType.refreshing){
}else{
LoadWorkComplete();
//
Operating=-1;
//
footerView.setRefreshStatus(StateType.none);
//
footerTopPadding(-footerContentHeight);
}
}
}
break;
case MotionEvent.ACTION_MOVE:
if((firstVisibleItemPosition==0 || LastVisibleItemPosition==totalItemCounts)&&!isRefreshing){
whenMove(ev);
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
private void doRefreshWork() {
isRefreshing = true;
if(mPushAndDownListviewRefreshOrLoad!=null){
mPushAndDownListviewRefreshOrLoad.Refresh();
}else{
new Handler().postDelayed(new Runnable() {
public void run() {
RefreshWorkComplete();
}
}, 3000);
}
}
private void doLoadWork() {
isRefreshing = true;
if(mPushAndDownListviewRefreshOrLoad!=null){
mPushAndDownListviewRefreshOrLoad.Load();
}else{
new Handler().postDelayed(new Runnable() {
public void run() {
LoadWorkComplete();
}
}, 3000);
}
}
//设置监听
public void setPushAndDownListviewRefreshOrLoadListener(PushAndDownListviewRefreshOrLoad listener){
this.mPushAndDownListviewRefreshOrLoad = listener;
}
//刷新完成后调用
public void RefreshWorkComplete(){
isRefreshing = false;
Operating=-1;
headView.setRefreshStatus(StateType.none);
headtopPadding(-headerContentHeight);
}
//加载完成后调用
public void LoadWorkComplete(){
isRefreshing = false;
Operating = -1;
footerView.setRefreshStatus(StateType.none);
footerTopPadding(-footerContentHeight);
}
private void whenMove(MotionEvent ev){
if(!isRecordedStartY || headView.getStateType()==StateType.refreshing || footerView.getStateType()==StateType.refreshing){
return;
}
float stempY = ev.getY();
int distanceY = (int)(stempY - startY);
if(distanceY > 0 && firstVisibleItemPosition == 0){
//执行刷新状态
doRefresh(distanceY);
}else if(distanceY < 0 && LastVisibleItemPosition==totalItemCounts){
//执行加载状态
doLoad(distanceY);
}
}
private void doRefresh(int distanceY){
Operating = Refresh;
if(distanceY-headerContentHeight < 0){
headView.setRefreshStatus(StateType.normal);
}else if(distanceY-headerContentHeight >= 0){
headView.setRefreshStatus(StateType.willrefresh);
}
headtopPadding(distanceY-headerContentHeight);
}
private void doLoad(int distanceY){
Operating = Load;
if(-(distanceY+footerContentHeight) < 0){
footerView.setRefreshStatus(StateType.normal);
}else if(-(distanceY+footerContentHeight) >= 0){
footerView.setRefreshStatus(StateType.willrefresh);
}
footerTopPadding(-(distanceY+footerContentHeight));
}
/**
* 测量view的方法
* @param view必须使用线性布局
*/
private void measureView(View clild) {
ViewGroup.LayoutParams p = clild.getLayoutParams();//获取layoutparams
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);//宽
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
}
clild.measure(childWidthSpec, childHeightSpec);//measure(0,0)必须使用线性布局,否则报空指针异常,因为线性布局重写了该方法,其他没有从写
}
/**
* 为headview从新设置padding
* @param topPadding
*/
private void headtopPadding(int topPadding) {
headView.setPadding(headView.getPaddingLeft(), topPadding,headView.getPaddingRight(), headView.getPaddingBottom());
headView.invalidate();
}
private void footerTopPadding(int topPadding){
footerView.setPadding(footerView.getPaddingLeft(), topPadding,footerView.getPaddingRight(), footerView.getPaddingBottom());
footerView.invalidate();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
this.firstVisibleItemPosition = firstVisibleItem;
this.LastVisibleItemPosition = firstVisibleItem+visibleItemCount;
this.totalItemCounts=totalItemCount;
}
//状态
enum StateType{
none, normal, willrefresh, refreshing
}
//自定义的状态显示view,用于添加到listview的头部和脚部
class StateBar extends LinearLayout{
//状态
StateType mStateType = StateType.none;
//显示的文字
private String normalString;
private String willrefreshString;
private String refreshingString;
public StateBar(Context context) {
super(context);
init(context);
}
public StateBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
//ui控件
ProgressBar progressBar;
TextView tv_state;
TextView tv_updatetime;
private void init(Context context){
LayoutInflater.from(context).inflate(R.layout.view_listviewheadorfoot, this);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
tv_state = (TextView) findViewById(R.id.tv_state);
tv_updatetime = (TextView) findViewById(R.id.tv_updatetime);
//设置状态
setRefreshStatus(mStateType);
}
//设置view的当前状态
public void setRefreshStatus(StateType refreshStatus) {
if (this.mStateType != refreshStatus) {
this.mStateType = refreshStatus;
if(mStateType == StateType.refreshing){
this.progressBar.setVisibility(View.VISIBLE);
}else{
this.progressBar.setVisibility(View.GONE);
}
refreshStatusString();
this.invalidate();
}
}
//刷新状态文字
private void refreshStatusString() {
switch (mStateType) {
case none:
case normal:
tv_state.setText(normalString);
break;
case willrefresh:
tv_state.setText(willrefreshString);
break;
case refreshing:
tv_state.setText(refreshingString);
break;
default:
break;
}
//这里先不做时间设置
tv_updatetime.setText("");
}
public StateType getStateType(){
return mStateType;
}
public void setStatusStrings(String normalString, String willrefreshString, String refreshingString){
this.normalString = normalString;
this.willrefreshString = willrefreshString;
this.refreshingString = refreshingString;
this.refreshStatusString();
}
}
interface PushAndDownListviewRefreshOrLoad{
void Refresh();
void Load();
}
}
有待完善。。。。。。
1.下拉刷新
实现原理:通过onTouchEvent判断手势,来改变listview的header。
header的状态共4种,自己定义为:none, normal, willrefresh, refreshing ,header在四种状态切换时不仅改变内部组件,同时改变自身的大小。
footer类似
实现:
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.windlu.anywhere.R;
/**
* 自定义的listview,实现上拉刷新和下拉加载
* @author lufeng
*
*/
public class PushAndDownListview extends ListView implements OnScrollListener{
private StateBar headView;//头部view
private StateBar footerView;//脚部view
private int headerContentHeight;//头部view的高度
private int footerContentHeight;//脚部view的高度
private static final int Refresh = 0;//刷新
private static final int Load = 1;//加载
private int Operating=-1;//操作
private PushAndDownListviewRefreshOrLoad mPushAndDownListviewRefreshOrLoad;//加载或刷新的回调接口
public PushAndDownListview(Context context) {
this(context,null);
}
public PushAndDownListview(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context){
//头部
headView = new StateBar(context);
headView.setStatusStrings(getResources().getString(R.string.pull_to_refresh),
getResources().getString(R.string.release_to_refresh),
getResources().getString(R.string.refreshing));
//测量view的高度
measureView(headView);
headerContentHeight=headView.getMeasuredHeight();
//设置view的toppadding值
headtopPadding(-headerContentHeight);
//添加view
addHeaderView(headView);
//脚部
footerView = new StateBar(context);
footerView.setStatusStrings(getResources().getString(R.string.pull_up_load),
getResources().getString(R.string.release_to_load),
getResources().getString(R.string.loading));
measureView(footerView);
footerContentHeight = footerView.getMeasuredHeight();
footerTopPadding(-footerContentHeight);
addFooterView(footerView);
}
float startY;
boolean isRecordedStartY;//是否已经记录StartY
int firstVisibleItemPosition ;
int LastVisibleItemPosition;
int totalItemCounts;
private boolean isRefreshing;
//主要在该方法中实现下拉刷新效果和上拉加载效果
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if((firstVisibleItemPosition==0 || LastVisibleItemPosition==totalItemCounts) && Operating == -1&&!isRefreshing){
startY = ev.getY();
isRecordedStartY=true;
return true;
}
break;
case MotionEvent.ACTION_UP:
isRecordedStartY=false;
if((firstVisibleItemPosition==0 || LastVisibleItemPosition==totalItemCounts) && Operating != -1&&!isRefreshing){
if(Operating == Refresh){
if(headView.getStateType()==StateType.willrefresh){
headView.setRefreshStatus(StateType.refreshing);
headtopPadding(0);
doRefreshWork();
}else if(headView.getStateType()==StateType.refreshing){
}else{
RefreshWorkComplete();
//
Operating=-1;
//
headView.setRefreshStatus(StateType.none);
//
headtopPadding(-headerContentHeight);
}
}else if(Operating == Load){
if(footerView.getStateType()==StateType.willrefresh){
footerView.setRefreshStatus(StateType.refreshing);
footerTopPadding(0);
doLoadWork();
}else if(footerView.getStateType()==StateType.refreshing){
}else{
LoadWorkComplete();
//
Operating=-1;
//
footerView.setRefreshStatus(StateType.none);
//
footerTopPadding(-footerContentHeight);
}
}
}
break;
case MotionEvent.ACTION_MOVE:
if((firstVisibleItemPosition==0 || LastVisibleItemPosition==totalItemCounts)&&!isRefreshing){
whenMove(ev);
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
private void doRefreshWork() {
isRefreshing = true;
if(mPushAndDownListviewRefreshOrLoad!=null){
mPushAndDownListviewRefreshOrLoad.Refresh();
}else{
new Handler().postDelayed(new Runnable() {
public void run() {
RefreshWorkComplete();
}
}, 3000);
}
}
private void doLoadWork() {
isRefreshing = true;
if(mPushAndDownListviewRefreshOrLoad!=null){
mPushAndDownListviewRefreshOrLoad.Load();
}else{
new Handler().postDelayed(new Runnable() {
public void run() {
LoadWorkComplete();
}
}, 3000);
}
}
//设置监听
public void setPushAndDownListviewRefreshOrLoadListener(PushAndDownListviewRefreshOrLoad listener){
this.mPushAndDownListviewRefreshOrLoad = listener;
}
//刷新完成后调用
public void RefreshWorkComplete(){
isRefreshing = false;
Operating=-1;
headView.setRefreshStatus(StateType.none);
headtopPadding(-headerContentHeight);
}
//加载完成后调用
public void LoadWorkComplete(){
isRefreshing = false;
Operating = -1;
footerView.setRefreshStatus(StateType.none);
footerTopPadding(-footerContentHeight);
}
private void whenMove(MotionEvent ev){
if(!isRecordedStartY || headView.getStateType()==StateType.refreshing || footerView.getStateType()==StateType.refreshing){
return;
}
float stempY = ev.getY();
int distanceY = (int)(stempY - startY);
if(distanceY > 0 && firstVisibleItemPosition == 0){
//执行刷新状态
doRefresh(distanceY);
}else if(distanceY < 0 && LastVisibleItemPosition==totalItemCounts){
//执行加载状态
doLoad(distanceY);
}
}
private void doRefresh(int distanceY){
Operating = Refresh;
if(distanceY-headerContentHeight < 0){
headView.setRefreshStatus(StateType.normal);
}else if(distanceY-headerContentHeight >= 0){
headView.setRefreshStatus(StateType.willrefresh);
}
headtopPadding(distanceY-headerContentHeight);
}
private void doLoad(int distanceY){
Operating = Load;
if(-(distanceY+footerContentHeight) < 0){
footerView.setRefreshStatus(StateType.normal);
}else if(-(distanceY+footerContentHeight) >= 0){
footerView.setRefreshStatus(StateType.willrefresh);
}
footerTopPadding(-(distanceY+footerContentHeight));
}
/**
* 测量view的方法
* @param view必须使用线性布局
*/
private void measureView(View clild) {
ViewGroup.LayoutParams p = clild.getLayoutParams();//获取layoutparams
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);//宽
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
}
clild.measure(childWidthSpec, childHeightSpec);//measure(0,0)必须使用线性布局,否则报空指针异常,因为线性布局重写了该方法,其他没有从写
}
/**
* 为headview从新设置padding
* @param topPadding
*/
private void headtopPadding(int topPadding) {
headView.setPadding(headView.getPaddingLeft(), topPadding,headView.getPaddingRight(), headView.getPaddingBottom());
headView.invalidate();
}
private void footerTopPadding(int topPadding){
footerView.setPadding(footerView.getPaddingLeft(), topPadding,footerView.getPaddingRight(), footerView.getPaddingBottom());
footerView.invalidate();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
this.firstVisibleItemPosition = firstVisibleItem;
this.LastVisibleItemPosition = firstVisibleItem+visibleItemCount;
this.totalItemCounts=totalItemCount;
}
//状态
enum StateType{
none, normal, willrefresh, refreshing
}
//自定义的状态显示view,用于添加到listview的头部和脚部
class StateBar extends LinearLayout{
//状态
StateType mStateType = StateType.none;
//显示的文字
private String normalString;
private String willrefreshString;
private String refreshingString;
public StateBar(Context context) {
super(context);
init(context);
}
public StateBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
//ui控件
ProgressBar progressBar;
TextView tv_state;
TextView tv_updatetime;
private void init(Context context){
LayoutInflater.from(context).inflate(R.layout.view_listviewheadorfoot, this);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
tv_state = (TextView) findViewById(R.id.tv_state);
tv_updatetime = (TextView) findViewById(R.id.tv_updatetime);
//设置状态
setRefreshStatus(mStateType);
}
//设置view的当前状态
public void setRefreshStatus(StateType refreshStatus) {
if (this.mStateType != refreshStatus) {
this.mStateType = refreshStatus;
if(mStateType == StateType.refreshing){
this.progressBar.setVisibility(View.VISIBLE);
}else{
this.progressBar.setVisibility(View.GONE);
}
refreshStatusString();
this.invalidate();
}
}
//刷新状态文字
private void refreshStatusString() {
switch (mStateType) {
case none:
case normal:
tv_state.setText(normalString);
break;
case willrefresh:
tv_state.setText(willrefreshString);
break;
case refreshing:
tv_state.setText(refreshingString);
break;
default:
break;
}
//这里先不做时间设置
tv_updatetime.setText("");
}
public StateType getStateType(){
return mStateType;
}
public void setStatusStrings(String normalString, String willrefreshString, String refreshingString){
this.normalString = normalString;
this.willrefreshString = willrefreshString;
this.refreshingString = refreshingString;
this.refreshStatusString();
}
}
interface PushAndDownListviewRefreshOrLoad{
void Refresh();
void Load();
}
}
有待完善。。。。。。
相关文章推荐
- android模拟器和真机区分
- sed + grep 解决 Android docs 打开慢的问题
- Android ListView存在多个item样式的处理方法
- min3d引擎使用指南(Android)<一>
- Android关闭自定义相机拍照声音
- android常用命令
- Android中实现用命令行同步网络时间
- Android Studio 依赖注入框架 butterknife
- Android bundle 传值方法
- Pro Android学习笔记(一七零):发布应用(3):打开商店、许可和ProGuard
- Android SD卡数据的读取
- 【iOS-Android开发对比】之 数据存储
- Android 23种设计模式
- Android 图片压缩也即生成缩略图方法
- android 图片格式和像素引发的问题
- 【Android】手势输入拨号器
- Android图像处理之Bitmap类
- Android开发之技术文章索引
- android下载安装APK
- Android中EditText中的InputType类型中文含义与如何定义