自定义ListView的下拉刷新和上拉加载更多
2016-08-06 09:50
399 查看
/** * Created by Ext on 2016/8/5. */ public class LoadMoreListView extends ListView implements AbsListView.OnScrollListener { private View mFooter; private View mHeader; private int mHeaderHeight; private int totalItemCount; private int lastVisibleItem; // 最后一个可见的item private int firstVisibleItem; private boolean isRemark; // 标记,当前是否在listView最顶端按下 private int startY; // 按下时的Y值 private int state; // 当前的状态 private final int NONE = 0; // 正常状态 private final int PULL = 1; // 提示下拉状态 private final int RELEASE = 2; // 提示释放状态 private final int REFRESHING = 3; // 刷新状态 private int scrollState; // 当前滚动状态 private TextView tipTv; private ImageView arrowIv; private ProgressBar progressBar; private TextView updateTimeTv; private boolean isLoading; private ILoadMoreListener mLoadMoreListener; private RotateAnimation animDown; private RotateAnimation animUp; private View mLoadingLayout; public LoadMoreListView(Context context) { this(context, null); } public LoadMoreListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LoadMoreListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } /** * 添加底部加载提示布局到ListView * @param context */ private void initView(Context context) { LayoutInflater inflater = LayoutInflater.from(context); mFooter = inflater.inflate(R.layout.footer_layout, null); mLoadingLayout = mFooter.findViewById(R.id.load_layout); mLoadingLayout.setVisibility(View.GONE); this.addFooterView(mFooter); mHeader = inflater.inflate(R.layout.header_layout, null); measureView(mHeader); mHeaderHeight = mHeader.getMeasuredHeight(); topPadding(-mHeaderHeight); this.addHeaderView(mHeader); this.setOnScrollListener(this); } /** * 通知父布局,占用的宽、高 * @param view */ private void measureView(View view) { ViewGroup.LayoutParams p = view.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } // 宽度测量规格 int width = ViewGroup.getChildMeasureSpec(0, 0, p.width); // 高度测量规格 int height; int tempHeight = p.height; if (tempHeight > 0) { height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY); } else { height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } view.measure(width, height); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: if (firstVisibleItem == 0) { isRemark = true; startY = (int) ev.getY(); } break; case MotionEvent.ACTION_MOVE: onMove(ev); break; case MotionEvent.ACTION_UP: // 松手后头布局处于两种状态 if (state == RELEASE) { state = REFRESHING; refreshViewByState(); // 加载数据 if (mLoadMoreListener != null) { mLoadMoreListener.onRefresh(); } } else if (state == PULL) { state = NONE; isRemark = false; refreshViewByState(); } break; } return super.onTouchEvent(ev); } /** * 判断移动过程操作 * @param ev */ private void onMove(MotionEvent ev){ if (!isRemark) { return; } int tempY = (int)ev.getY(); // space 摁下后滑动的垂直距离 int space = tempY - startY; int topPadding = space - mHeaderHeight; if (topPadding > mHeaderHeight + 20) { topPadding = mHeaderHeight + 20; } switch (state) { case NONE: if (space > 0) { state = PULL; refreshViewByState(); } break; case PULL: // 滑动距离大于一定高度之后就更新状态 topPadding(topPadding); if (space > mHeaderHeight + 10 && scrollState == SCROLL_STATE_TOUCH_SCROLL) { state = RELEASE; refreshViewByState(); } break; case RELEASE: // 限制头部布局的topPadding if (topPadding >= mHeaderHeight + 20) { topPadding = mHeaderHeight; } topPadding(topPadding); if (space < mHeaderHeight + 10) { state = PULL; refreshViewByState(); } else if (space <= 0) { state = NONE; isRemark = false; refreshViewByState(); } break; case REFRESHING: break; } } /** * 根据当前状态,改变界面显示(只要在滑动时状态变化就需要调用) */ private void refreshViewByState() { if (tipTv == null) { tipTv = (TextView) mHeader.findViewById(R.id.tip_tv); } if (arrowIv == null) { arrowIv = (ImageView) mHeader.findViewById(R.id.arrow); } if (progressBar == null) { progressBar = (ProgressBar) mHeader.findViewById(R.id.progress); } if (animDown == null) { animDown = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animDown.setDuration(500); animDown.setFillAfter(true); } if (animUp == null) { animUp = new RotateAnimation(180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animUp.setDuration(500); animUp.setFillAfter(true); } switch (state) { case NONE: topPadding(-mHeaderHeight); break; case PULL: arrowIv.setVisibility(VISIBLE); progressBar.setVisibility(GONE); tipTv.setText("下拉可以刷新"); arrowIv.clearAnimation(); arrowIv.setAnimation(animDown); break; case RELEASE: arrowIv.setVisibility(VISIBLE); progressBar.setVisibility(GONE); tipTv.setText("松开可以刷新"); arrowIv.clearAnimation(); arrowIv.setAnimation(animUp); break; case REFRESHING: topPadding(10); arrowIv.setVisibility(GONE); progressBar.setVisibility(VISIBLE); tipTv.setText("正在刷新"); arrowIv.clearAnimation(); break; } } /** * 加载完更多数据 */ public void loadComplete() { isLoading = false; mLoadingLayout.setVisibility(View.GONE); invalidate(); } /** * 获取完数据 */ public void refreshComplete() { state = NONE; isRemark = false; refreshViewByState(); if (updateTimeTv == null) { updateTimeTv = (TextView) mHeader.findViewById(R.id.time_tv); } SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss"); Date date = new Date(System.currentTimeMillis()); updateTimeTv.setText(format.format(date)); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 针对加载更多 if (totalItemCount == lastVisibleItem && scrollState == SCROLL_STATE_IDLE) { if (!isLoading) { isLoading = true; mLoadingLayout.setVisibility(View.VISIBLE); if (mLoadMoreListener != null) { mLoadMoreListener.onLoadMore(); } } } // 针对下拉刷新 this.scrollState = scrollState; } /** * * @param view * @param firstVisibleItem 第一个可见的item的数据在数据集中的索引 * @param visibleItemCount 当前屏幕可见的item总数 * @param totalItemCount 数据集中数据的总数 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.firstVisibleItem = firstVisibleItem; this.lastVisibleItem = firstVisibleItem + visibleItemCount; this.totalItemCount = totalItemCount; } private void topPadding(int topPadding) { mHeader.setPadding(mHeader.getPaddingLeft(), topPadding, mHeader.getPaddingRight(), mHeader.getPaddingBottom()); } public void setLoadMoreListener(ILoadMoreListener mLoadMoreListener) { this.mLoadMoreListener = mLoadMoreListener; } public interface ILoadMoreListener { public void onLoadMore(); public void onRefresh(); } }
相关文章推荐
- 护眼必要操作
- {"errcode":40097,"errmsg":"invalid args hint: [vjNe7xxxxxx8vr19]"}——记录一次微信错误处理
- 技术解析:DAS、SAN和NAS三种存储方式
- POJ 2585 Window Pains(窗口的颜色显示问题,拓扑排序,经典题目)
- 安卓应用kill 自身并启动
- android OpenGL的一些知识
- Android emulator使用方法
- 背景图合并用在什么地方最适合,有什么优点
- 深入理解JS DOM事件机制
- 初始速度
- Java中的String与正则表达式浅述
- if示例_shell脚本
- 转载:程序员必看的十大电影
- Tomcat部署web项目,如何直接通过域名访问,不加项目名称
- 有点激动,WPF换肤搞定了!
- (复习)图论--最短路--Floyd算法
- MVC思想和三层架构
- 洛谷 P2258 NOIP2014普及组 T4 子矩阵 DFS+DP
- 浅谈DOS与DDOS攻击的原理
- js script加载