自定义AutoListView全部文件
(PS:不是项目包,个人认为这样更容易学习和使用): https://download.csdn.net/download/rlw_1009/10579396
直接上代码,自定义代码如下:
package com.lwre.myandroidtest.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import cn.sec.watchcarecloud.R;
import cn.sec.watchcarecloud.utils.SystemUtils;
/**
* Created by lwren
*/
public class AutoListView extends ListView implements AbsListView.OnScrollListener {
// 区分当前操作是刷新还是加载
public static final int REFRESH = 10;
public static final int LOAD = 11;
// 定义header的四种状态和当前状态
private static final int NONE = 0;
private static final int PULL = 1;
private static final int RELEASE = 2;
private static final int REFRESH_END = 3;
//private int state;
private int state = NONE;
private RotateAnimation animation;
private RotateAnimation reverseAnimation;
private LayoutInflater inflater;
//头部
private View header;
private TextView tip;
private TextView lastUpdate;
private ImageView arrow;
//底部
private View footer;
private TextView noData;
private TextView loadFull;
private TextView more;
private TextView pull_to_load;
//头部和底部高度
private int headerContentInitialHeight;
private int headerContentHeight;
private int footViewHeight;
//下拉刷新判断
private boolean isRecorded = false; //根据是否firstVisibleItem==0,判断是否可以操作下拉刷新
//上拉加载判断
private boolean isLoadOperation = false; //是否是“上拉加载操作”
private boolean loadEnable = true; // 开启或者关闭加载更多功能
private boolean isLoading = false; // 判断是否正在加载
private boolean isLoadFull = false; //是否加载全部数据
private int scrollState; //记录滑动状态,此处 仅用于上拉加载
private int startY; //手指'第一次'触摸屏幕y点坐标值
private int space; //手指滑动距离,正数:下拉刷新;负数:下拉加载
private static final int SPACE = 100; // 区分PULL和RELEASE的距离的大小
private int pageSize = 10; //一次加载的数据数量
private int firstVisibleItem; //记录显示的第一个item是第几个
private OnRefreshListener onRefreshListener;
private OnLoadListener onLoadListener;
public AutoListView(Context context) {
super(context);
initView(context);
}
public AutoListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public AutoListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
// 设置箭头特效
animation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(100);
animation.setFillAfter(true);
reverseAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseAnimation.setInterpolator(new LinearInterpolator());
reverseAnimation.setDuration(100);
reverseAnimation.setFillAfter(true);
inflater = LayoutInflater.from(context);
//为listview加入Header
header = inflater.inflate(R.layout.header_view, null);
arrow = (ImageView) header.findViewById(R.id.arrow);
tip = (TextView) header.findViewById(R.id.tip);
lastUpdate = (TextView) header.findViewById(R.id.lastUpdate);
//为listview加入Footer
footer = inflater.inflate(R.layout.listview_footer, null);
loadFull = (TextView) footer.findViewById(R.id.loadFull);
noData = (TextView) footer.findViewById(R.id.noData);
more = (TextView) footer.findViewById(R.id.more);
pull_to_load = (TextView) footer.findViewById(R.id.pull_to_load);
// loading = (ProgressBar) footer.findViewById(R.id.loading);
footer.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
}
});
// 为listview添加头部和尾部,并进行初始化
headerContentInitialHeight = header.getPaddingTop();
measureView(header);
headerContentHeight = header.getMeasuredHeight();
headerTopPadding(-headerContentHeight);
//测量footview的高度,通过measure()传入两个0参数,系统会不认这个0,自动帮我们测量出加在底部View的长度信息
footer.measure(0, 0);
//拿到高度
footViewHeight = footer.getMeasuredHeight();
//隐藏view,让头部显示高度为实际测量高度的负数,实现布局的隐藏 //footer.setPadding(0, -footViewHeight, 0, 0);
footerheaderTopPadding(-footViewHeight);
this.addHeaderView(header);
this.addFooterView(footer);
this.setOnScrollListener(this);
}
/*******************Begin***下拉刷新***********************************************************************************************/
/**
* 监听触摸事件,解读手势
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
if (firstVisibleItem == 0) {
isRecorded = true;
//startY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
whenMove(ev);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (state == PULL) {
state = NONE;
refreshHeaderViewByState();
} else if (state == RELEASE) {
state = REFRESH_END;
refreshHeaderViewByState();
//!!!必须firstVisibleItem==0时才可以执行【刷新操作】
if (firstVisibleItem == 0) {
//执行【刷新操作】
onRefresh();
}
}
break;
}
return super.onTouchEvent(ev);
}
// 解读手势,刷新header状态
private void whenMove(MotionEvent ev) {
if (!isRecorded) {
return;
}
//手指正在触摸的y点坐标
int tmpY = (int) ev.getY();
//手指滑动距离(正数:下拉刷新;负数:下拉加载)=‘手指正在触摸的y点坐标’减去‘手指"第一次"触摸屏幕y点坐标值’
space = tmpY - startY;
//手指滑动距离 与 headerview高度的差值
int topPadding = space - headerContentHeight;
switch (state) {
case NONE:
if (space > 0) {
state = PULL;
}
break;
case PULL:
//实时显示下拉的header // topPadding: 手指滑动距离 与 headerview高度的差值
headerTopPadding(topPadding);
/** OnScrollListener.SCROLL_STATE_IDLE: 视图不滚动。 注意使用轨迹球导航列表计为处于空闲状态,因为这些转换不是动画的。
* OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 用户使用触摸滚动,他们的手指仍然在屏幕上
* OnScrollListener.SCROLL_STATE_FLING: 用户之前使用触摸滚动并执行了一次投掷。 动画现在正在停止
*/
// 如果“用户使用触摸滚动,他们的手指仍然在屏幕上”,并且“手指滑动距离 与 headerview高度的差值”大于 SPACE ,执行下一步RELEASE状态
//if (scrollState == SCROLL_STATE_TOUCH_SCROLL && topPadding > SPACE) {
// 如果“用户使用触摸滚动,他们的手指仍然在屏幕上”,并且“手指滑动距离space 大于(headerview高度加"最小距离")”,执行下一步RELEASE状态
if (scrollState == SCROLL_STATE_TOUCH_SCROLL && space > (headerContentHeight + SPACE)) {
state = RELEASE;
refreshHeaderViewByState();
}
break;
case RELEASE:
//实时显示下拉的header // topPadding: 手指滑动距离 与 headerview高度的差值
headerTopPadding(topPadding);
//如果“手指滑动距离space>0”,但是“手指滑动距离space 小于(headerview高度加"最小距离")”,则返回上一步状态,不执行【刷新操作】
if (space > 0 && space < headerContentHeight + SPACE) {
state = PULL;
refreshHeaderViewByState();
} else if (space <= 0) { //如果“手指滑动距离space=0”,则返回“初始NONE状态”,不执行【刷新操作】
state = NONE;
refreshHeaderViewByState();
}
break;
//进行到RELEASE状态后,在onTouchEvent的‘ACTION_UP’‘ACTION_UP’进行判断是否执行【刷新操作】
}
}
// 根据当前状态,调整header
private void refreshHeaderViewByState() {
switch (state) {
case NONE:
headerTopPadding(-headerContentHeight);
tip.setText(R.string.pull_to_refresh); //下拉可以刷新
arrow.clearAnimation();
arrow.setImageResource(R.drawable.pull_to_refresh_arrow);
break;
case PULL:
arrow.setVisibility(View.VISIBLE);
tip.setVisibility(View.VISIBLE);
lastUpdate.setVisibility(View.VISIBLE);
//提示:下拉可以刷新
tip.setText(R.string.pull_to_refresh);
//箭头添加“向下的动画”
arrow.clearAnimation();
arrow.setAnimation(reverseAnimation);
break;
case RELEASE:
arrow.setVisibility(View.VISIBLE);
tip.setVisibility(View.VISIBLE);
lastUpdate.setVisibility(View.VISIBLE);
tip.setText(R.string.release_to_refresh); //提示:松开可以刷新
//箭头添加“向上的动画”
arrow.clearAnimation();
arrow.setAnimation(animation);
break;
case REFRESH_END:
//HeaderView初始状态/恢复初始状态
initialHeaderState();
break;
}
}
/**
* HeaderView初始状态/恢复初始状态
*/
private void initialHeaderState() {
/***********下拉刷新恢复初始状态**********/
//记录最近更新的时间
onRefreshComplete();
//根据是否firstVisibleItem==0,判断是否可以操作下拉刷新,恢复默认false
isRecorded = false;
state = NONE;
//是否是“上拉加载操作”
isLoadOperation = false;
//是否加载全部数据
// 此参数比较特殊,在【上拉加载】要保留状态,根据setResultSize(int resultSize)加载的数量判断生效;
//在此处【下拉刷新】要恢复默认值,否则不能再次进行“上拉加载”
isLoadFull = false;
//恢复“默认或初始化”headerview
arrow.setVisibility(View.GONE);
tip.setVisibility(View.GONE);
lastUpdate.setVisibility(View.GONE);
refreshHeaderViewByState();
}
/**
* 记录最近更新的时间
*/
public void onRefreshComplete() {
String currentTime = SystemUtils.getCurrentTime();
lastUpdate.setText(this.getContext().getString(R.string.lastUpdateTime, SystemUtils.getCurrentTime()));
}
/*******************End***下拉刷新***********************************************************************************************/
// 用来计算header大小的。比较隐晦。因为header的初始高度就是0,貌似可以不用。
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
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);
}
child.measure(childWidthSpec, childHeightSpec);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.firstVisibleItem = firstVisibleItem;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState = scrollState;
ifNeedLoad(view, scrollState);
}
/*##################Begin###上拉加载##############################################################################################*/
// 根据listview滑动的状态判断是否需要加载更多
private void ifNeedLoad(AbsListView view, int scrollState) {
if (!loadEnable) { // loadEnable: 开启或者关闭加载更多功能
return;
}
try {
/** OnScrollListener.SCROLL_STATE_IDLE: 视图不滚动。 注意使用轨迹球导航列表计为处于空闲状态,因为这些转换不是动画的。
* OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 用户使用触摸滚动,他们的手指仍然在屏幕上
* OnScrollListener.SCROLL_STATE_FLING: 用户之前使用触摸滚动并执行了一次投掷。 动画现在正在停止
*/
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
&& !isLoading
&& view.getLastVisiblePosition() == view.getPositionForView(footer)
&& !isLoadFull //是否加载全部数据
&& space < -(footViewHeight + SPACE)) {
//执行【下拉加载】
onLoad();
// 是否正在加载
isLoading = true;
}
} catch (Exception e) {
}
}
/**
* 用于加载更多结束后的回调
*/
public void onLoadComplete() {
//FooterView初始状态/恢复初始状态
initialFooterState();
}
/**
* FooterView初始状态/恢复初始状态
*/
private void initialFooterState() {
isLoadOperation = true;
loadEnable = true; // 开启或者关闭加载更多功能
isLoading = false; // 判断是否正在加载
//isLoadFull = false; //是否加载全部数据 //此参数比较特殊,要保留状态,根据setResultSize(int resultSize)加载的数量判断生效
/***********上拉加载恢复初始状态**********/
loadFull.setVisibility(View.GONE);
more.setVisibility(View.GONE);
noData.setVisibility(View.GONE);
footerheaderTopPadding(-footViewHeight);
}
/**
* 这个方法是根据结果的大小来决定footer显示的。
*
* 这里假定每次请求的条数为10。如果请求到了10条。则认为还有数据。如过结果不足10条,则认为数据已经全部加载,这时footer显示已经全部加载
*
footerheaderTopPadding(footViewHeight);
//isLoadOperation:是否是“上拉加载操作”
if (!isLoadOperation) { //*************下拉刷新操作*************
if (resultSize == pageSize) {
//显示“上拉加载更多”
loadFull.setVisibility(View.GONE);
more.setVisibility(View.GONE);
noData.setVisibility(View.GONE);
pull_to_load.setVisibility(VISIBLE);
} else if (resultSize == 0) {
//显示“暂无数据”
isLoadFull = true;
loadFull.setVisibility(View.GONE);
more.setVisibility(View.GONE);
noData.setVisibility(View.VISIBLE);
pull_to_load.setVisibility(GONE);
} else if (resultSize > 0 && resultSize < pageSize) {
//显示“已加载全部”
isLoadFull = true;
loadFull.setVisibility(View.VISIBLE);
more.setVisibility(View.GONE);
noData.setVisibility(View.GONE);
pull_to_load.setVisibility(GONE);
}
} else { //#############上拉加载操作#############
if (resultSize == 0 || (resultSize > 0 && resultSize < pageSize)) {
//显示“已加载全部”
isLoadFull = true;
loadFull.setVisibility(View.VISIBLE);
more.setVisibility(View.GONE);
noData.setVisibility(View.GONE);
pull_to_load.setVisibility(GONE);
} else if (resultSize == pageSize) {
//显示“上拉加载更多”
loadFull.setVisibility(View.GONE);
more.setVisibility(View.GONE);
noData.setVisibility(View.GONE);
pull_to_load.setVisibility(VISIBLE);
}
}
}
/*##################End###上拉加载##############################################################################################*/
// 调整header的大小。其实调整的只是距离顶部的高度。
private void headerTopPadding(int headerTopPadding) {
header.setPadding(header.getPaddingLeft(), headerTopPadding, header.getPaddingRight(), header.getPaddingBottom());
header.invalidate();
}
// 调整footer的显示
private void footerheaderTopPadding(int headerTopPadding) {
footer.setPadding(header.getPaddingLeft(), headerTopPadding, header.getPaddingRight(), header.getPaddingBottom());
footer.invalidate();
}
/*
* 定义下拉刷新接口
*/
public interface OnRefreshListener {
public void onRefresh();
}
/*
* 定义加载更多接口
*/
public interface OnLoadListener {
public void onLoad();
}
// 下拉刷新监听
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
this.onRefreshListener = onRefreshListener;
}
// 加载更多监听
public void setOnLoadListener(OnLoadListener onLoadListener) {
this.loadEnable = true;
this.onLoadListener = onLoadListener;
}
public void onRefresh() {
if (onRefreshListener != null) {
onRefreshListener.onRefresh();
}
}
public void onLoad() {
if (onLoadListener != null) {
onLoadListener.onLoad();
}
}
}