自定义ListView的下拉刷新和上拉加载更多
来源:互联网 发布:centos指令大全 编辑:程序博客网 时间:2024/05/13 17:26
/** * 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(); }}
0 0
- 自定义ListView的下拉刷新和上拉加载更多
- listview的下拉刷新和上拉加载更多
- listview的上拉加载更多和下拉刷新
- ListView下拉刷新和上拉加载更多的实现
- 自定义控件之ListView下拉刷新,上拉加载更多
- Android 自定义下拉刷新上拉加载更多Listview
- 自定义ListView实现下拉刷新,上拉加载更多
- 自定义ListView下拉刷新上拉加载更多功能
- 自定义ListView上拉刷新,下拉加载更多(一)
- Android 自定义下拉刷新上拉加载更多Listview
- Listview 下拉刷新,上拉加载更多
- Listview下拉刷新上拉加载更多
- 下拉刷新,上拉加载更多ListView
- ListView下拉刷新,上拉加载更多
- ListView下拉刷新上拉加载更多
- 自定义控件-下拉刷新和上拉加载的listView
- 自定义listview的下拉刷新和上拉加载
- [Android]下拉刷新和上拉加载更多Listview,仿知乎
- FreeCMS视频教程-jdk环境搭建
- 闭包是什么?作用域垃圾回收机制混合理解闭包
- 单例模式
- 关于免流的.ovpn文件说明
- Java中的泛型 集合(Map) IO流
- 自定义ListView的下拉刷新和上拉加载更多
- SourceTree 各历史版本下载地址
- Spring+Spring MVC整合
- Set Matrix Zeroes
- 2003-Can't connect to MySQL server on 'localhost'(10038)
- Swift 如何获取类的Class
- Caffe学习(四)数据层及参数设置
- poj2001(字典树)
- php接口编写