Android 下拉/上拉刷新/左滑删除控件(2)
2018-03-06 15:36
309 查看
代码复制即可用
这个控件的下拉刷新的实现是自定义的
这个控件的下拉刷新的实现是自定义的
package com.cx.wdiget.listView; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; 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.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import com.cx.wdiget.R; import com.mcxtzhang.swipemenulib.SwipeMenuLayout; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; /** * 自定义listView下拉和上拉刷新 * <p/> * listView中item左滑删除采用这个第三方{@link SwipeMenuLayout}控件 */ public class CListView extends ListView implements AbsListView.OnScrollListener { private LayoutInflater inflate; private View footerRootView; private View headerRootView; private ProgressBar pb_footerProgressBar; private TextView tv_footerHint; private TextView tv_headRefreshTips; private TextView tv_headRefreshLastUpdateTime; private TextView tv_headRefreshSuccess; private ImageView iv_headRefreshArrow; private ImageView iv_headRefreshProgress; private RelativeLayout rl_headRefreshContentLayout; private IUpPullRefreshListener iUpPullRefreshListener; private IDownPullRefreshListener iDownPullRefreshListener; /** * headView箭头动画 */ private RotateAnimation mArrowAnim; /** * headView箭头反转动画 */ private RotateAnimation mArrowReverseAnim; /** * 上拉加载更多状态码 * <p/> * UP_LOADING 加载中 * <p/> * UP_LOADING_DONE 加载完成 * <p/> * UP_LOADING_NO_MORE 加载完成没有更多新数据,数据加载成功没有下一页数据时显示 * <p/> * UP_LOADING_ERROR 加载完成异常(无网络、获取数据失败等) */ private int upLoadingState = 0; private final int UP_LOADING = 1; private final int UP_LOADING_DONE = 2; private final int UP_LOADING_NO_MORE = 3; private final int UP_LOADING_ERROR = 4; /** * DOWN_RELEASE_LOADING 下拉松开刷新状态 * <p/> * DOWN_PULL_LOADING 下拉刷新状态 * <p/> * DOWN_LOADING 下拉刷新中 * <p/> * DOWN_LOADING_DONE 下刷新完成 */ private int downLoadingState = 5; private final static int DOWN_RELEASE_LOADING = 6; private final static int DOWN_PULL_LOADING = 7; private final static int DOWN_LOADING = 8; private final static int DOWN_LOADING_DONE = 9; /**headerView高度**/ private int mHeadViewHeight; /**listView 可见的第一个item下标**/ private int mFirstVisibleItem = -1; /**下拉刷新时 记录第一次downY的位置**/ private int mStartY = -1; /** * 下拉刷新实际距离和界面便宜距离的比例,造成一种拉伸感 **/ private final static int downPullRatio = 3; /** * 当前item是否超过手机一屏幕,超过才把footerView预加载出来 **/ private boolean moreThanOneScreen = false; /** * 当前item是否是滑动到屏幕最后一条 **/ private boolean scrollLastItem = false; /** * 使用了{@link SwipeMenuLayout}控件后,是否有item滑动开,false关闭 true 打开 **/ private boolean swipeMenuLayoutOpen = false; /** * 当上拉加载,没有更多数据时,记录加载时间,做一个几分钟内再次执行上拉不加载数据的限制 **/ private long upNoMoreDataStartTime = 0; public CListView(Context context) { this(context, null); } public CListView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } /** * 初始化操作 * * @param context context */ private void initView(Context context) { inflate = LayoutInflater.from(context); this.setOnScrollListener(this); initHeaderView(); initPullImageAnimation(0); } /** * listView 初始化FooterView */ private void initFooterView() { footerRootView = inflate.inflate(R.layout.item_up_pull_refresh_footer, null); pb_footerProgressBar = (ProgressBar) footerRootView.findViewById(R.id.pb_footerProgressBar); tv_footerHint = (TextView) footerRootView.findViewById(R.id.tv_footerHint); footerRootView.setVisibility(View.GONE); footerRootView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { upPullLoadingData(true); } }); addFooterView(footerRootView); } /** * 添加下拉刷新的HeadView */ private void initHeaderView() { headerRootView = inflate.inflate(R.layout.listview_refresh_header, null); iv_headRefreshArrow = (ImageView) headerRootView.findViewById(R.id.iv_headRefreshArrow); iv_headRefreshArrow.setMinimumWidth(60); iv_headRefreshArrow.setMinimumHeight(60); iv_headRefreshProgress = (ImageView) headerRootView.findViewById(R.id.iv_headRefreshProgress); tv_headRefreshTips = (TextView) headerRootView.findViewById(R.id.tv_headRefreshTips); tv_headRefreshLastUpdateTime = (TextView) headerRootView.findViewById(R.id.tv_headRefreshLastUpdateTime); tv_headRefreshSuccess= (TextView) headerRootView.findViewById(R.id.tv_headRefreshSuccess); rl_headRefreshContentLayout= (RelativeLayout) headerRootView.findViewById(R.id.rl_headRefreshContentLayout); measureView(headerRootView); mHeadViewHeight = headerRootView.getMeasuredHeight(); headerRootView.setPadding(0, -1 * mHeadViewHeight, 0, 0); headerRootView.invalidate(); addHeaderView(headerRootView, null, false); downLoadingState = DOWN_LOADING_DONE; } /** * IM聊天 加载历史数据使用 */ public void setChat() { RelativeLayout rl_chat = (RelativeLayout) headerRootView.findViewById(R.id.rl_chatProgress); rl_chat.setVisibility(View.VISIBLE); headerRootView.setBackgroundColor(getResources().getColor(R.color.color_ffffff)); } /** * 测量HeadView宽高 */ private void measureView(View pChild) { ViewGroup.LayoutParams p = pChild.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childWidthSpec = ViewGroup.getChildMeasureSpec(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); } pChild.measure(childWidthSpec, childHeightSpec); } /** * on touch event * * @param event event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: swipeMenuLayoutOpen = (SwipeMenuLayout.getViewCache() != null); //当左滑打开时不能执行任何非当前item的事件 if (swipeMenuLayoutOpen) { this.requestDisallowInterceptTouchEvent(true); SwipeMenuLayout.getViewCache().smoothClose(); } getDowPullStartY((int) event.getY()); break; case MotionEvent.ACTION_MOVE: this.requestDisallowInterceptTouchEvent(swipeMenuLayoutOpen); downPullMoveEvent(event); break; case MotionEvent.ACTION_UP: swipeMenuLayoutOpen = false; downPullUpEvent(); break; case MotionEvent.ACTION_CANCEL: swipeMenuLayoutOpen = false; break; default: break; } return swipeMenuLayoutOpen || super.onTouchEvent(event); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE && scrollLastItem) { upPullLoadingData(false); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstVisibleItem = firstVisibleItem; moreThanOneScreen = (totalItemCount > visibleItemCount); scrollLastItem = (firstVisibleItem + visibleItemCount == totalItemCount); //满足条件先把footerRootView显示出来,滑动到底部时不会造成footerRootView显示的延迟 //iUpPullRefreshListener表示用户允许执行上拉加载 //moreThanOneScreen当列表数据超过一屏幕时才展示 if (iUpPullRefreshListener != null && footerRootView != null) { footerRootView.setVisibility(moreThanOneScreen ? View.VISIBLE : View.GONE); } } /** * 记录下拉刷新时第一次点击到屏幕的Y坐标 * <p/> * 只记录一次mStartY的值 * <p/> * up的时候还原mStartY=-1; * <p/> * 这个方法会在down下去的时候执行一次(如果刚好item处于第0个则记录y轴值,move的时候就不再执行) * <p/> * 如果down下去item不是处于第0个,我们move滑动到第一个时也要执行此方法 * * @param y y */ private void getDowPullStartY(int y) { if (swipeMenuLayoutOpen || iDownPullRefreshListener == null) { return; } //当item处于第一个的时候我们就记录mStartY的坐标 if (mFirstVisibleItem == 0 && mStartY == -1) { mStartY = y; } } /** * 下拉刷新move事件代码逻辑 */ private void downPullMoveEvent(MotionEvent event) { int tempY = (int) event.getY(); getDowPullStartY(tempY); if (downLoadingState != DOWN_LOADING && mStartY != -1) { // 以松手去刷新了 if (downLoadingState == DOWN_RELEASE_LOADING) { // 保证在设置padding的过程中,当前的位置一直是在head, // 否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动 setSelection(0); // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步 if (((tempY - mStartY) / downPullRatio < mHeadViewHeight) && (tempY - mStartY) > 0) { downLoadingState = DOWN_PULL_LOADING; changeHeaderViewByState(); } // 一下子推到顶了 else if (tempY - mStartY <= 0) { downLoadingState = DOWN_LOADING_DONE; changeHeaderViewByState(); } else { headerRootView.setPadding(0, (tempY - mStartY) / downPullRatio - mHeadViewHeight, 0, 0); } } // 还没有到达显示松开刷新的时候 if (downLoadingState == DOWN_PULL_LOADING) { setSelection(0); // 下拉到进入松手刷新状态 if ((tempY - mStartY) / downPullRatio >= mHeadViewHeight) { downLoadingState = DOWN_RELEASE_LOADING; changeHeaderViewByState(); } else if (tempY - mStartY <= 0) { downLoadingState = DOWN_LOADING_DONE; changeHeaderViewByState(); } else { headerRootView.setPadding(0, (tempY - mStartY) / downPullRatio - mHeadViewHeight, 0, 0); } } //刷新完成的状态 if (downLoadingState == DOWN_LOADING_DONE) { if (tempY - mStartY > 0) { downLoadingState = DOWN_PULL_LOADING; changeHeaderViewByState(); } } } } /** * 上拉刷新执行up事件的代码逻辑 */ private void downPullUpEvent() { if (downLoadingState != DOWN_LOADING) { if (downLoadingState == DOWN_PULL_LOADING) { downLoadingState = DOWN_LOADING_DONE; changeHeaderViewByState(); } if (downLoadingState == DOWN_RELEASE_LOADING) { downLoadingState = DOWN_LOADING; changeHeaderViewByState(); } } mStartY = -1; } private void changeHeaderViewByState() { if (headerRootView == null || iUpPullRefreshListener == null) { return; } switch (downLoadingState) { case DOWN_LOADING: headerRootView.setPadding(0, 0, 0, 0); headRefreshProgressAnimation(true); iv_headRefreshArrow.clearAnimation(); tv_headRefreshTips.setText("正在刷新"); iUpPullRefreshListener.upPullRefresh(); downPullLoadingComplete(); break; case DOWN_LOADING_DONE: headerRootView.setPadding(0, -1 * mHeadViewHeight, 0, 0); headRefreshProgressAnimation(false); iv_headRefreshArrow.clearAnimation(); iv_headRefreshArrow.setImageResource(R.mipmap.xia); tv_headRefreshTips.setText("下拉刷新"); break; case DOWN_RELEASE_LOADING: headRefreshProgressAnimation(false); iv_headRefreshArrow.clearAnimation(); iv_headRefreshArrow.startAnimation(mArrowAnim); tv_headRefreshTips.setText("松开刷新"); break; case DOWN_PULL_LOADING: headRefreshProgressAnimation(false); tv_headRefreshTips.setText("下拉刷新"); iv_headRefreshArrow.clearAnimation(); iv_headRefreshArrow.startAnimation(mArrowReverseAnim); break; default: break; } } /** * 上拉刷新状态的改变,改变样式 */ private void changeFooterViewByState() { if (iUpPullRefreshListener == null || footerRootView == null) { return; } switch (upLoadingState) { case UP_LOADING_NO_MORE: pb_footerProgressBar.setVisibility(View.GONE); tv_footerHint.setText("没有更多了"); if (upNoMoreDataStartTime <= 0) { upNoMoreDataStartTime = System.currentTimeMillis(); } break; case UP_LOADING_ERROR: pb_footerProgressBar.setVisibility(View.GONE); tv_footerHint.setText("点击加载更多"); upNoMoreDataStartTime = 0; break; case UP_LOADING: pb_footerProgressBar.setVisibility(View.VISIBLE); tv_footerHint.setText("加载中..."); upNoMoreDataStartTime = 0; break; default: break; } } /** * 执行上拉加载数据 */ private void upPullLoadingData(boolean clickFooterView) { if (iUpPullRefreshListener != null && upLoadingState != UP_LOADING && moreThanOneScreen) { if (!AndroidUtils.isNetworkAvailable()) { AndroidUtils.toast("网络不给力,请检查后重试"); upLoadingState = UP_LOADING_ERROR; changeFooterViewByState(); return; } if (upLoadingState == UP_LOADING_NO_MORE && !againLoadingNoMoreData()) { return; } if (upLoadingState == UP_LOADING_ERROR && !clickFooterView) { return; } upLoadingState = UP_LOADING; changeFooterViewByState(); iUpPullRefreshListener.upPullRefresh(); } } /** * 实例化下拉刷新的箭头的动画效果 * * @param pAnimDuration 动画运行时长 * @date 2013-11-20 上午11:53:22 * @change JohnWatson * @version 1.0 */ private void initPullImageAnimation(final int pAnimDuration) { int _Duration; if (pAnimDuration > 0) { _Duration = pAnimDuration; } else { _Duration = 250; } Interpolator _Interpolator = new LinearInterpolator(); mArrowAnim = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mArrowAnim.setInterpolator(_Interpolator); mArrowAnim.setDuration(_Duration); mArrowAnim.setFillAfter(true); mArrowReverseAnim = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mArrowReverseAnim.setInterpolator(_Interpolator); mArrowReverseAnim.setDuration(_Duration); mArrowReverseAnim.setFillAfter(true); } /** * 超过五分钟才能再次执行上拉加载 * <p/> * 当上拉加载状态为UP_LOADING_NO_MORE时,再次执行上拉加载时有时间限制 * <p/> * 超过限制时间后才能再次执行上拉操作 * * @return true 可以加载 false不可以加载 */ private boolean againLoadingNoMoreData() { return (System.currentTimeMillis() - upNoMoreDataStartTime > (5 * 60 * 1000)); } /** * 数据加载完成后调用 * * @param loadSuccess 加载数据是否成功 * @param nextPage 是否还有下一页数据 */ public void loadDataOver(boolean loadSuccess, boolean nextPage) { Log.v("tag", "loadDataOver"); if (loadSuccess) { if (!nextPage) { upLoadingState = UP_LOADING_NO_MORE; changeFooterViewByState(); } else { upLoadingState = UP_LOADING_DONE; } } else { upLoadingState = UP_LOADING_ERROR; changeFooterViewByState(); } } Handler mHandler = new Handler(); /** * 下拉刷新完成 */ public void downPullLoadingComplete() { new Thread() { public void run() { try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.post(new Runnable() { @Override public void run() { rl_headRefreshContentLayout.setVisibility(View.GONE); tv_headRefreshSuccess.setText(AndroidUtils.isNetworkAvailable()?"刷新成功":"网络不给力请检查后重试!"); } }); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.post(new Runnable() { @Override public void run() { rl_headRefreshContentLayout.setVisibility(View.VISIBLE); tv_headRefreshLastUpdateTime.setText("上次更新:" + new SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.CHINA).format(new Date())); downLoadingState = DOWN_LOADING_DONE; changeHeaderViewByState(); } }); } }.start(); } public void headRefreshProgressAnimation(boolean isStart) { iv_headRefreshProgress.setVisibility(isStart ? VISIBLE : GONE); iv_headRefreshArrow.setVisibility(isStart ? GONE : VISIBLE); AnimationDrawable animationDrawable = (AnimationDrawable) iv_headRefreshProgress.getBackground(); if (isStart) { animationDrawable.start(); } else { animationDrawable.stop(); } } public void setUpPullRefreshListener(IUpPullRefreshListener iUpPullRefreshListener) { if (iUpPullRefreshListener != null) { this.iUpPullRefreshListener = iUpPullRefreshListener; initFooterView(); } } public void setDownPullRefreshListener(IDownPullRefreshListener iDownPullRefreshListener) { this.iDownPullRefreshListener = iDownPullRefreshListener; } public interface IUpPullRefreshListener { void upPullRefresh(); } public interface IDownPullRefreshListener { void downPullRefresh(); } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@color/color_f5f7f7" android:orientation="horizontal"> <RelativeLayout android:id="@+id/rl_chatProgress" android:layout_width="match_parent" android:layout_height="50dp" android:visibility="gone"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:orientation="horizontal"> <ProgressBar android:id="@+id/pb_chatProgressBar" style="?android:attr/progressBarStyleSmall" android:layout_width="@dimen/dimen_15dp" android:layout_height="@dimen/dimen_15dp" android:indeterminate="true" android:indeterminateDrawable="@drawable/up_pull_footer_progressbar" android:visibility="visible" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginLeft="5dp" android:text="加载中..." /> </LinearLayout> </RelativeLayout> <TextView android:visibility="gone" android:id="@+id/tv_headRefreshSuccess" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center" android:text="刷新成功!" android:textColor="#747b81" android:textSize="12sp" /> <RelativeLayout android:id="@+id/rl_headRefreshContentLayout" android:layout_width="match_parent" android:layout_height="50dp"> <!-- 箭头图像、进度条 --> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="18dp" android:layout_toLeftOf="@+id/ll_"> <ImageView android:id="@+id/iv_headRefreshArrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:contentDescription="@string/app_name" android:src="@mipmap/xia" /> <ImageView android:id="@+id/iv_headRefreshProgress" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center" android:background="@drawable/animation_loading_circle" android:visibility="gone" /> </FrameLayout> <LinearLayout android:id="@+id/ll_" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center_horizontal" android:orientation="vertical"> <TextView android:id="@+id/tv_headRefreshTips" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="松开刷新" android:textColor="#747b81" android:textSize="12sp" /> <TextView android:id="@+id/tv_headRefreshLastUpdateTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="6dp" android:text="上次更新:" android:textColor="#747b81" android:textSize="9sp" /> </LinearLayout> </RelativeLayout> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:background="@color/color_ffffff" android:gravity="center" android:orientation="horizontal" android:padding="@dimen/dimen_15dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal"> <ProgressBar android:id="@+id/pb_footerProgressBar" style="?android:attr/progressBarStyleSmall" android:layout_width="@dimen/dimen_15dp" android:layout_height="@dimen/dimen_15dp" android:indeterminate="true" android:indeterminateDrawable="@drawable/up_pull_footer_progressbar" android:visibility="visible" /> <TextView android:id="@+id/tv_footerHint" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/dimen_10dp" android:gravity="center" android:text="@string/p2refresh_head_load_more" android:textColor="#00c0c7" android:textSize="@dimen/dimen_14sp" /> </LinearLayout> </LinearLayout>
相关文章推荐
- Android 下拉/上拉刷新/左滑删除控件
- 【Android界面实现】ZListView,一个最强大的刷新、加载、滑动删除的ListView控件(二)
- [ Android界面实现 ] ZListView,一个最强大的刷新、加载、滑动删除的 ListView 控件(一)
- 【Android界面实现】ZListView,一个最强大的刷新、加载、滑动删除的ListView控件(二)
- Android SwipeRefreshLayout 官方下拉刷新控件介绍
- 【Android开发】随意拖动控件,刷新不回到原位
- Android SwipeRefreshLayout 官方下拉刷新控件介绍
- android gridview listview控件内容刷新 而不是整个页面刷新
- Android高级控件之ListView的优化以及下拉刷新页面
- Android水平刷新控件-HorizontalRefreshLayout
- Android 解决在图片库删除图片后,无法实时刷新问题的解决!
- Android控件Gridview实现多个menu模块,可添加可删除
- Android 仿QQ未读消息拖拽删除粘性控件效果
- Android高级控件(一)——ListView绑定CheckBox实现全选,增加和删除等功能
- Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能
- Android基础控件——RecyclerView实现拖拽排序侧滑删除效果
- android自定义控件(组合控件)--带删除按钮的EditText
- Android SwipeRefreshLayout 官方下拉刷新控件介绍
- 想要亲手实现一个Android刷新控件,你只需要掌握这些知识
- 发者使用 HorizontalRefreshLayout-Android 可以对 RecycView、Listview、ScrollView 等控件实现左右刷新