绪论

最近项目里面用到了下拉刷新和左滑删除,网上找了找并没有可以用的,有比较好的左滑删除,但是并没有和下拉刷新上拉加载结合到一起,要不就是一些比较水的结合,并不能在项目里面使用,小编一着急自己组合了一个,做完了和QQ的对比了一下,并没有太大区别,今天分享给大家,其实并不难,但是不知道为什么网上没有比较好的Demo,当你的项目真的很急的时候,又没有比较好的Demo,那么“那条友谊的小船儿真是说翻就翻啊”,好了,下面先来具体看一下实现后的效果吧:

代码已经上传到Github上了,小伙伴们记得star和follow哦
https://github.com/Hankkin/SwipeRefreshDemo


还不错吧?比QQ多了个上拉加载,好了看看怎么实现的吧,小编在之前的游客评论中了解到,代码注释很重要,所以尽量把注释写的很清楚:

实现思路

由于时间有限,左滑菜单是在网上找的Demo

  1. 自定义下拉刷新头、尾
  2. 手势判断,根据滑动距离显示头部下拉布局
  3. 判断是否滑动到底部显示尾部上拉布局
  4. 创建左滑菜单,根据手势滑动事件弹出菜单

详细的看一下实现过程
1.首先我们先自定义下拉头布局:

<?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:gravity="bottom" ><RelativeLayout
        android:id="@+id/xlistview_header_content"android:layout_width="fill_parent"android:background="#00000000"android:layout_height="60dp" ><LinearLayout
            android:id="@+id/xlistview_header_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:gravity="center"android:orientation="vertical" ><TextView
                android:id="@+id/xlistview_header_hint_textview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="#000"android:textSize="16sp"android:text="@string/xlistview_header_hint_normal" /><LinearLayout
                android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="3dp" ><TextView
                    android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="#000"android:text="@string/xlistview_header_last_time"android:textSize="14sp" /><TextView
                    android:id="@+id/xlistview_header_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="#000"android:textSize="14sp" /></LinearLayout></LinearLayout><ImageView
            android:id="@+id/xlistview_header_arrow"android:layout_width="wrap_content"android:layout_height="35dp"android:layout_alignLeft="@id/xlistview_header_text"android:layout_centerVertical="true"android:layout_marginLeft="-50dp"android:src="@drawable/xlistview_arrow" /><ProgressBar
            android:id="@+id/xlistview_header_progressbar"android:layout_width="25dp"android:layout_height="25dp"android:layout_alignLeft="@id/xlistview_header_text"android:layout_centerVertical="true"android:layout_marginLeft="-55dp"android:visibility="gone"/></RelativeLayout></LinearLayout>

2.接下来我们自定义HeaderView,代码很详细了,不多介绍了

package com.hankkin.library;import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;public class RefreshListHeader extends LinearLayout {//根布局private LinearLayout mContainer;//下拉箭头图片private ImageView mArrowImageView;//下拉进度条private ProgressBar mProgressBar;//下拉文本private TextView mHintTextView;//状态值 0-正常 1-准备刷新  2-正在刷新private int mState = STATE_NORMAL;//抬起动画private Animation mRotateUpAnim;//下拉动画private Animation mRotateDownAnim;//下拉动画时间private final int ROTATE_ANIM_DURATION = 180;public final static int STATE_NORMAL = 0;public final static int STATE_READY = 1;public final static int STATE_REFRESHING = 2;public RefreshListHeader(Context context) {super(context);initView(context);}/*** @param context* @param attrs*/public RefreshListHeader(Context context, AttributeSet attrs) {super(context, attrs);initView(context);}/*** 初始化组件* @param context*/private void initView(Context context) {LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, 0);mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.xlistview_header, null);addView(mContainer, lp);setGravity(Gravity.BOTTOM);mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);mProgressBar = (ProgressBar) findViewById(R.id.xlistview_header_progressbar);//设置抬起动画mRotateUpAnim = new RotateAnimation(0.0f, -180.0f, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);mRotateUpAnim.setFillAfter(true);//设置下拉动画mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);mRotateDownAnim.setFillAfter(true);}//设置状态public void setState(int state) {if (state == mState)return;//正在刷新时-显示进度条,隐藏下拉箭头if (state == STATE_REFRESHING) {mArrowImageView.clearAnimation();mArrowImageView.setVisibility(View.INVISIBLE);mProgressBar.setVisibility(View.VISIBLE);} else {//显示下拉箭头,隐藏进度条mArrowImageView.setVisibility(View.VISIBLE);mProgressBar.setVisibility(View.INVISIBLE);}switch (state) {case STATE_NORMAL:if (mState == STATE_READY) {//准备刷新时开启动画mArrowImageView.startAnimation(mRotateDownAnim);}if (mState == STATE_REFRESHING) {//正在刷新时开启动画mArrowImageView.clearAnimation();}mHintTextView.setText(R.string.xlistview_header_hint_normal);break;case STATE_READY:if (mState != STATE_READY) {mArrowImageView.clearAnimation();mArrowImageView.startAnimation(mRotateUpAnim);mHintTextView.setText(R.string.xlistview_header_hint_ready);}break;case STATE_REFRESHING:mHintTextView.setText(R.string.xlistview_header_hint_loading);break;default:}mState = state;}/*** 设置头部高度* @param height*/public void setVisiableHeight(int height) {if (height < 0)height = 0;LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();lp.height = height;mContainer.setLayoutParams(lp);}public int getVisiableHeight() {return mContainer.getHeight();}}

3.HeaderView定义结束后,我们需要自定义一个既支持下拉刷新又支持左滑删除的ListView,看看我是怎么做的:(左滑菜单是引用网上的Demo,代码写的也比较易懂,这里不详细给大家介绍了)
然后我们在他的基础上添加下拉上拉事件:(重点看一下onTouchEvent事件)

@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (mLastY == -1) { //获取上次y轴坐标mLastY = ev.getRawY();}switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:   //手势按下事件、获取坐标、设置上次下拉时间firstTouchY = ev.getRawY();mLastY = ev.getRawY();setRefreshTime(RefreshTime.getRefreshTime(getContext()));int oldPos = mTouchPosition;mDownX = ev.getX();mDownY = ev.getY();mTouchState = TOUCH_STATE_NONE;mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());//弹出左滑菜单if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {mTouchState = TOUCH_STATE_X;mTouchView.onSwipe(ev);//左滑菜单手势监听事件,根据滑动距离弹出菜单return true;}//获取item view,此方法是因为getChildAt()传入index值导致listview不可见的item会报空指针// 防止listview不可见的item获取到的为空,使用下面方法View view = getChildAt(mTouchPosition - getFirstVisiblePosition());if (mTouchView != null && mTouchView.isOpen()) {//如果滑动的item不为空并且已经开启,则关闭该菜单mTouchView.smoothCloseMenu();mTouchView = null;return super.onTouchEvent(ev);}if (mTouchView != null) {//否则打开左滑菜单mTouchView.onSwipe(ev);}if (view instanceof SwipeMenuLayout) {mTouchView = (SwipeMenuLayout) view;}break;case MotionEvent.ACTION_MOVE://手势滑动事件final float deltaY = ev.getRawY() - mLastY;float dy = Math.abs((ev.getY() - mDownY));float dx = Math.abs((ev.getX() - mDownX));mLastY = ev.getRawY();//判断左滑菜单是否未激活、或者x轴偏移平方小于y轴偏移平方3倍的时候if ((mTouchView == null || !mTouchView.isActive()) && Math.pow(dx, 2) / Math.pow(dy, 2) <= 3) {//判断第一个可见位置并且头部布局可见高度大于0时或者y轴偏移量>0if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {// 重新更新头部高度updateHeaderHeight(deltaY / OFFSET_RADIO);invokeOnScrolling();}}if (mTouchState == TOUCH_STATE_X) {//如果x轴偏移弹出左滑菜单if (mTouchView != null) {mTouchView.onSwipe(ev);}getSelector().setState(new int[] { 0 });ev.setAction(MotionEvent.ACTION_CANCEL);super.onTouchEvent(ev);return true;} else if (mTouchState == TOUCH_STATE_NONE) {if (Math.abs(dy) > MAX_Y) { //如果y轴偏移量>指定y轴偏移量,设置y轴偏移状态mTouchState = TOUCH_STATE_Y;} else if (dx > MAX_X) {//如果x轴偏移量>指定x轴偏移量,设置x轴偏移状态,开始弹出左滑菜单mTouchState = TOUCH_STATE_X;if (mOnSwipeListener != null) {mOnSwipeListener.onSwipeStart(mTouchPosition);}}}break;case MotionEvent.ACTION_UP://手势抬起事件mLastY = -1; // resetif (getFirstVisiblePosition() == 0) {// 设置下拉刷新状态值,开启下拉刷新状态if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {mPullRefreshing = true;mHeaderView.setState(RefreshListHeader.STATE_REFRESHING);if (onRefreshListener != null) {tag=REFRESH;onRefreshListener.onRefresh();}}resetHeaderHeight();}lastTouchY = ev.getRawY();//获取上次y轴偏移量if (canLoadMore()) {//判断是否满足上拉loadData();}if (mTouchState == TOUCH_STATE_X) {//如果为x轴偏移状态,开启左滑if (mTouchView != null) {mTouchView.onSwipe(ev);if (!mTouchView.isOpen()) {mTouchPosition = -1;mTouchView = null;}}if (mOnSwipeListener != null) {mOnSwipeListener.onSwipeEnd(mTouchPosition);}ev.setAction(MotionEvent.ACTION_CANCEL);super.onTouchEvent(ev);return true;}break;}return super.onTouchEvent(ev);}

当然这部分也是滑动冲突解决的主要部分,看一下完整的代码,应该能够看的懂了,小编几乎每一句都加上了注释,如果有看不明白的地方可以留言,小编会及时回复的:

package com.hankkin.library;import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;import java.util.Date;public class RefreshSwipeMenuListView extends ListView implements OnScrollListener {private static final int TOUCH_STATE_NONE = 0;private static final int TOUCH_STATE_X = 1; //x轴触摸状态值private static final int TOUCH_STATE_Y = 2; //y轴触摸状态值public static final int BOTH=2;//上拉和下拉public static final int HEADER=0;//下拉public static final int FOOTER=1;//上拉public static String tag;//ListView的动作public static final String REFRESH="refresh";public static final String LOAD="load";private int MAX_Y = 5;  //Y轴最大偏移量private int MAX_X = 3;  //X轴最大偏移量private float mDownX;   //触摸xprivate float mDownY;   //触摸yprivate int mTouchState;    //触摸状态private int mTouchPosition; //触摸位置private SwipeMenuLayout mTouchView; //滑动弹出布局private OnSwipeListener mOnSwipeListener;   //弹出监听器private float firstTouchY;  //第一次触摸y坐标private float lastTouchY;   //最后一次触摸y坐标//创建左滑菜单接口private SwipeMenuCreator mMenuCreator;//菜单点击事件private OnMenuItemClickListener mOnMenuItemClickListener;//关闭菜单动画修饰Interpolatorprivate Interpolator mCloseInterpolator;//开启菜单动画修饰Interpolatorprivate Interpolator mOpenInterpolator;private float mLastY = -1;private Scroller mScroller;private OnScrollListener mScrollListener; // 滑动监听// 下拉上拉监听器private OnRefreshListener onRefreshListener;//下拉头private RefreshListHeader mHeaderView;//头部视图内容,用来计算头部高度,不下拉时隐藏private RelativeLayout mHeaderViewContent;//下拉时间文本控件private TextView mHeaderTimeView;private int mHeaderViewHeight; // 头部高度private boolean mEnablePullRefresh = true;//能否下拉刷新private boolean mPullRefreshing = false; // 是否正在刷新//上拉尾部视图private LinearLayout mFooterView;private boolean mEnablePullLoad;//是否可以上拉加载private boolean mPullLoading;   //是否正在上拉private boolean mIsFooterReady = false;private int mTotalItemCount;private int mScrollBack;private final static int SCROLLBACK_HEADER = 0;private final static int SCROLLBACK_FOOTER = 1;private final static int SCROLL_DURATION = 400;private final static int PULL_LOAD_MORE_DELTA = 50;private final static float OFFSET_RADIO = 1.8f;private boolean isFooterVisible=false;public RefreshSwipeMenuListView(Context context) {super(context);init(context);}public RefreshSwipeMenuListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}public RefreshSwipeMenuListView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}/*** 初始化组件* @param context*/private void init(Context context) {mScroller = new Scroller(context, new DecelerateInterpolator());super.setOnScrollListener(this);// 初始化头部视图mHeaderView = new RefreshListHeader(context);mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content);mHeaderTimeView = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time);addHeaderView(mHeaderView);// 初始化尾部视图mFooterView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.xlistview_footer, null, false);// 初始化头部高度mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {mHeaderViewHeight = mHeaderViewContent.getHeight();//向 ViewTreeObserver 注册方法,以获取控件尺寸getViewTreeObserver().removeGlobalOnLayoutListener(this);}});MAX_X = dp2px(MAX_X);MAX_Y = dp2px(MAX_Y);mTouchState = TOUCH_STATE_NONE;}/*** 添加适配器* @param adapter*/@Overridepublic void setAdapter(ListAdapter adapter) {if (mIsFooterReady == false) {  //添加尾部隐藏mIsFooterReady = true;addFooterView(mFooterView);mFooterView.setVisibility(GONE);}super.setAdapter(new SwipeMenuAdapter(getContext(), adapter) {@Overridepublic void createMenu(SwipeMenu menu) {//创建左滑菜单if (mMenuCreator != null) {mMenuCreator.create(menu);}}@Overridepublic void onItemClick(SwipeMenuView view, SwipeMenu menu, int index) {if (mOnMenuItemClickListener != null) {//左滑菜单点击事件mOnMenuItemClickListener.onMenuItemClick(view.getPosition(), menu, index);}if (mTouchView != null) {mTouchView.smoothCloseMenu();}}});}public void setCloseInterpolator(Interpolator interpolator) {mCloseInterpolator = interpolator;}public void setOpenInterpolator(Interpolator interpolator) {mOpenInterpolator = interpolator;}public Interpolator getOpenInterpolator() {return mOpenInterpolator;}public Interpolator getCloseInterpolator() {return mCloseInterpolator;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (mLastY == -1) { //获取上次y轴坐标mLastY = ev.getRawY();}switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:   //手势按下事件、获取坐标、设置上次下拉时间firstTouchY = ev.getRawY();mLastY = ev.getRawY();setRefreshTime(RefreshTime.getRefreshTime(getContext()));int oldPos = mTouchPosition;mDownX = ev.getX();mDownY = ev.getY();mTouchState = TOUCH_STATE_NONE;mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());//弹出左滑菜单if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {mTouchState = TOUCH_STATE_X;mTouchView.onSwipe(ev);//左滑菜单手势监听事件,根据滑动距离弹出菜单return true;}//获取item view,此方法是因为getChildAt()传入index值导致listview不可见的item会报空指针// 防止listview不可见的item获取到的为空,使用下面方法View view = getChildAt(mTouchPosition - getFirstVisiblePosition());if (mTouchView != null && mTouchView.isOpen()) {//如果滑动的item不为空并且已经开启,则关闭该菜单mTouchView.smoothCloseMenu();mTouchView = null;return super.onTouchEvent(ev);}if (mTouchView != null) {//否则打开左滑菜单mTouchView.onSwipe(ev);}if (view instanceof SwipeMenuLayout) {mTouchView = (SwipeMenuLayout) view;}break;case MotionEvent.ACTION_MOVE://手势滑动事件final float deltaY = ev.getRawY() - mLastY;float dy = Math.abs((ev.getY() - mDownY));float dx = Math.abs((ev.getX() - mDownX));mLastY = ev.getRawY();//判断左滑菜单是否未激活、或者x轴偏移平方小于y轴偏移平方3倍的时候if ((mTouchView == null || !mTouchView.isActive()) && Math.pow(dx, 2) / Math.pow(dy, 2) <= 3) {//判断第一个可见位置并且头部布局可见高度大于0时或者y轴偏移量>0if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {// 重新更新头部高度updateHeaderHeight(deltaY / OFFSET_RADIO);invokeOnScrolling();}}if (mTouchState == TOUCH_STATE_X) {//如果x轴偏移弹出左滑菜单if (mTouchView != null) {mTouchView.onSwipe(ev);}getSelector().setState(new int[] { 0 });ev.setAction(MotionEvent.ACTION_CANCEL);super.onTouchEvent(ev);return true;} else if (mTouchState == TOUCH_STATE_NONE) {if (Math.abs(dy) > MAX_Y) { //如果y轴偏移量>指定y轴偏移量,设置y轴偏移状态mTouchState = TOUCH_STATE_Y;} else if (dx > MAX_X) {//如果x轴偏移量>指定x轴偏移量,设置x轴偏移状态,开始弹出左滑菜单mTouchState = TOUCH_STATE_X;if (mOnSwipeListener != null) {mOnSwipeListener.onSwipeStart(mTouchPosition);}}}break;case MotionEvent.ACTION_UP://手势抬起事件mLastY = -1; // resetif (getFirstVisiblePosition() == 0) {// 设置下拉刷新状态值,开启下拉刷新状态if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {mPullRefreshing = true;mHeaderView.setState(RefreshListHeader.STATE_REFRESHING);if (onRefreshListener != null) {tag=REFRESH;onRefreshListener.onRefresh();}}resetHeaderHeight();}lastTouchY = ev.getRawY();//获取上次y轴偏移量if (canLoadMore()) {//判断是否满足上拉loadData();}if (mTouchState == TOUCH_STATE_X) {//如果为x轴偏移状态,开启左滑if (mTouchView != null) {mTouchView.onSwipe(ev);if (!mTouchView.isOpen()) {mTouchPosition = -1;mTouchView = null;}}if (mOnSwipeListener != null) {mOnSwipeListener.onSwipeEnd(mTouchPosition);}ev.setAction(MotionEvent.ACTION_CANCEL);super.onTouchEvent(ev);return true;}break;}return super.onTouchEvent(ev);}class ResetHeaderHeightTask extends AsyncTask<Void, Void, Void> {protected Void doInBackground(Void... params) {try {Thread.sleep(400);} catch (InterruptedException e) {e.printStackTrace();}return null;}protected void onPostExecute(Void result) {mPullRefreshing = false;mHeaderView.setState(RefreshListHeader.STATE_NORMAL);resetHeaderHeight();}}public void smoothOpenMenu(int position) {if (position >= getFirstVisiblePosition() && position <= getLastVisiblePosition()) {View view = getChildAt(position - getFirstVisiblePosition());if (view instanceof SwipeMenuLayout) {mTouchPosition = position;if (mTouchView != null && mTouchView.isOpen()) {mTouchView.smoothCloseMenu();}mTouchView = (SwipeMenuLayout) view;mTouchView.smoothOpenMenu();}}}private int dp2px(int dp) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,getContext().getResources().getDisplayMetrics());}public void setMenuCreator(SwipeMenuCreator menuCreator) {this.mMenuCreator = menuCreator;}public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {this.mOnMenuItemClickListener = onMenuItemClickListener;}public void setOnSwipeListener(OnSwipeListener onSwipeListener) {this.mOnSwipeListener = onSwipeListener;}public static interface OnMenuItemClickListener {void onMenuItemClick(int position, SwipeMenu menu, int index);}public static interface OnSwipeListener {void onSwipeStart(int position);void onSwipeEnd(int position);}/*** 设置刷新可用* @param enable*/private void setPullRefreshEnable(boolean enable) {mEnablePullRefresh = enable;if (!mEnablePullRefresh) { // disable, hide the contentmHeaderViewContent.setVisibility(View.INVISIBLE);} else {mHeaderViewContent.setVisibility(View.VISIBLE);}}/*** enable or disable pull up load more feature.* 设置加载可用* @param enable*/private void setPullLoadEnable(boolean enable) {mEnablePullLoad = enable;if (!mEnablePullLoad) {mFooterView.setVisibility(GONE);mFooterView.setOnClickListener(null);} else {mPullLoading = false;mFooterView.setVisibility(VISIBLE);// both "pull up" and "click" will invoke load more.mFooterView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {startLoadMore();}});}}/*** stop refresh, reset header view.* 停止刷新,重置头部控件*/private void stopRefresh() {if (mPullRefreshing == true) {mPullRefreshing = false;resetHeaderHeight();}}/*** stop load more, reset footer view.* 停止加载更多,重置尾部控件*/private void stopLoadMore() {if (mPullLoading == true) {mPullLoading = false;mFooterView.setVisibility(GONE);}}/*** set last refresh time** @param time*/public void setRefreshTime(String time) {mHeaderTimeView.setText(time);}private void invokeOnScrolling() {if (mScrollListener instanceof OnXScrollListener) {OnXScrollListener l = (OnXScrollListener) mScrollListener;l.onXScrolling(this);}}/*** 更新头部高度,设置状态值* @param delta*/private void updateHeaderHeight(float delta) {mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight());if (mEnablePullRefresh && !mPullRefreshing) {if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {mHeaderView.setState(RefreshListHeader.STATE_READY);} else {mHeaderView.setState(RefreshListHeader.STATE_NORMAL);}}setSelection(0); // scroll to top each time}/*** 重置头部视图高度*/private void resetHeaderHeight() {int height = mHeaderView.getVisiableHeight();if (height == 0) // 不可见return;// 如果正在刷新并且头部高度没有完全显示不做操作if (mPullRefreshing && height <= mHeaderViewHeight) {return;}int finalHeight = 0; // 默认//如果正在刷新并且滑动高度大于头部高度if (mPullRefreshing && height > mHeaderViewHeight) {finalHeight = mHeaderViewHeight;}mScrollBack = SCROLLBACK_HEADER;mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION);// 触发computescrollinvalidate();}/*** 开启上啦*/private void startLoadMore() {mPullLoading = true;mFooterView.setVisibility(VISIBLE);if (onRefreshListener != null) {tag=LOAD;onRefreshListener.onLoadMore();}}@Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {if (mScrollBack == SCROLLBACK_HEADER) {mHeaderView.setVisiableHeight(mScroller.getCurrY());} postInvalidate();invokeOnScrolling();}super.computeScroll();}@Overridepublic void setOnScrollListener(OnScrollListener l) {mScrollListener = l;}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (mScrollListener != null) {mScrollListener.onScrollStateChanged(view, scrollState);}}/*** 滑动监听* @param view* @param firstVisibleItem* @param visibleItemCount* @param totalItemCount*/@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {mTotalItemCount = totalItemCount;if (mScrollListener != null) {mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);}if(firstVisibleItem+visibleItemCount>=totalItemCount){isFooterVisible=true;}else{isFooterVisible=false;}}public void setOnRefreshListener(OnRefreshListener l) {onRefreshListener = l;}/*** you can listen ListView.OnScrollListener or this one. it will invoke* onXScrolling when header/footer scroll back.*/public interface OnXScrollListener extends OnScrollListener {public void onXScrolling(View view);}/*** implements this interface to get refresh/load more event.*/public interface OnRefreshListener {public void onRefresh();public void onLoadMore();}/*** 上拉加载和下拉刷新请求完毕*/public void complete(){stopLoadMore();stopRefresh();if(REFRESH.equals(tag)){RefreshTime.setRefreshTime(getContext(),new Date());}}/*** 设置ListView的模式,上拉和下拉* @param mode*/public void setListViewMode(int mode){if(mode==BOTH){setPullRefreshEnable(true);setPullLoadEnable(true);}else if(mode==FOOTER){setPullLoadEnable(true);}else if(mode==HEADER){setPullRefreshEnable(true);}}/*** 判断是否可以上蜡加载* @return*/private boolean canLoadMore() {return isBottom() && !mPullLoading && isPullingUp();}/*** 判断是否到达底部* @return*/private boolean isBottom() {if (getCount() > 0) {if (getLastVisiblePosition() == getAdapter().getCount() - 1 &&getChildAt(getChildCount() - 1).getBottom() <= getHeight()) {return true;}}return false;}private boolean isPullingUp() {return (firstTouchY - lastTouchY) >= 200;}private void loadData() {if (onRefreshListener != null) {setLoading(true);}}/*** 设置是否上拉* @param loading*/public void setLoading(boolean loading) {if (this == null) return;mPullLoading = loading;if (loading) {mFooterView.setVisibility(VISIBLE);setSelection(getAdapter().getCount() - 1);onRefreshListener.onLoadMore();} else {mFooterView.setVisibility(GONE);firstTouchY = 0;lastTouchY = 0;}}}

定义完了之后我们是这样用的:

rsmLv.setAdapter(adapter);rsmLv.setListViewMode(RefreshSwipeMenuListView.HEADER);rsmLv.setOnRefreshListener(this);SwipeMenuCreator creator = new SwipeMenuCreator() {@Overridepublic void create(SwipeMenu menu) {// 创建滑动选项SwipeMenuItem rejectItem = new SwipeMenuItem(getApplicationContext());// 设置选项背景rejectItem.setBackground(new ColorDrawable(getResources().getColor(R.color.top)));// 设置选项宽度rejectItem.setWidth(dp2px(80,getApplicationContext()));// 设置选项标题rejectItem.setTitle("置顶");// 设置选项标题rejectItem.setTitleSize(16);// 设置选项标题颜色rejectItem.setTitleColor(Color.WHITE);// 添加选项menu.addMenuItem(rejectItem);// 创建删除选项SwipeMenuItem argeeItem = new SwipeMenuItem(getApplicationContext());argeeItem.setBackground(new ColorDrawable(getResources().getColor(R.color.del)));argeeItem.setWidth(dp2px(80, getApplicationContext()));argeeItem.setTitle("删除");argeeItem.setTitleSize(16);argeeItem.setTitleColor(Color.WHITE);menu.addMenuItem(argeeItem);}};rsmLv.setMenuCreator(creator);rsmLv.setOnMenuItemClickListener(new RefreshSwipeMenuListView.OnMenuItemClickListener() {@Overridepublic void onMenuItemClick(int position, SwipeMenu menu, int index) {switch (index) {case 0: //第一个选项Toast.makeText(MainActivity.this,"您点击的是置顶",Toast.LENGTH_SHORT).show();break;case 1: //第二个选项del(position,rsmLv.getChildAt(position+1-rsmLv.getFirstVisiblePosition()));break;}}});

为了和QQ有着一样的体验效果,小编加了一个删除item的动画
看起来就不会太生硬了,用户体验也比较好:

/*** 删除item动画* @param index* @param v*/private void del(final int index, View v){final Animation animation = (Animation) AnimationUtils.loadAnimation(v.getContext(), R.anim.list_anim);animation.setAnimationListener(new Animation.AnimationListener() {public void onAnimationStart(Animation animation) {}public void onAnimationRepeat(Animation animation) {}public void onAnimationEnd(Animation animation) {data.remove(index);adapter.notifyDataSetChanged();animation.cancel();}});v.startAnimation(animation);}

好了到这里了,我们的组合控件-上拉下拉+左滑删除的组合ListView就定义好了,分享出来小伙伴们以后遇到可以直接拿来用,”到时候小船儿也不会再翻了”,不合理的地方希望大家提出来,共同交流,进步。

代码已经上传到Github上了,小伙伴们记得star和follow哦
https://github.com/Hankkin/SwipeRefreshDemo

Android自定义组合控件---教你如何自定义下拉刷新和左滑删除相关推荐

  1. ajax 下拉刷新 上拉加载更多,局部刷新iscroll控件的具体使用(下拉刷新,上拉加载更多)...

    标签下,这是调用iscroll的必要条件,否则会出现不能刷新或者不能滑动页面的情况.此外还有商量刷新及下拉刷新的标签,在iscroll中有这两个标签的样式,若不想用默认的样式,也可通过覆盖的方式添加其 ...

  2. Android 天气APP(十)继续优化、下拉刷新页面天气数据

    上一篇:Android 天气APP(九)细节优化.必应每日一图 修复每日一图,增加下拉刷新,滑动改变标题 新版------------------- 一.修复每日请求必应壁纸Bug 二.增加下拉刷新 ...

  3. Android 手机卫士--自定义组合控件构件布局结构

    由于设置中心条目中的布局都很类似,所以可以考虑使用自定义组合控件来简化实现 本文地址:http://www.cnblogs.com/wuyudong/p/5909043.html,转载请注明源地址. ...

  4. Android View体系(十)自定义组合控件

    相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...

  5. Android自定义组合控件--EditText和Button组合成带有清空EditText内容功能的复合控件

    目标:实现EditText和Button组合成带有清空EditText内容功能的复合控件,可以通过代码设置自定义控件的相关属性. 实现效果为: (1)在res/layout目录下编写自定义组合控件的布 ...

  6. Android自定义组合布局,Android 流式布局 + 自定义组合控件

    自定义组合控件 package yanjupeng.bawei.com.day09.two; import android.content.Context; import android.util.A ...

  7. Android 自定义组合控件小结

    Android 自定义组合控件小结 引言 接触Android UI开发的这段时间以来,对自定义组合控件有了一定的了解,为此小结一下,本文小结内容主要讨论的是如何使用Android SDK提供的布局和控 ...

  8. Android Studio自定义组合控件

    在Android的开发中,为了能够服用代码,会把有一定共有特点的控件组合在一起定义成一个自定义组合控件.  本文就详细讲述这一过程.虽然这样的View的组合有一个粒度的问题.粒度太大了无法复用,粒度太 ...

  9. Android自定义View 自定义组合控件

    自定义组合控件: 以三国杀游戏武将为例,包括武将头像,血条,装备区 1.先定义该组合的XML文件布局 1 <?xml version="1.0" encoding=" ...

最新文章

  1. 计算机科学技术作文600,生活因科技而精彩作文600字
  2. window.open
  3. 职场不设限:真正的AI产品经理太少了……
  4. 天津计算机考研901,2013年天津大学901计算机考研真题
  5. mfc制作登录界面mysql_MFC制作漂亮界面之登录界面
  6. 给字符串对象定义一个repeat功能。当传入一个整数n时,它会返回重复n次字符串的结果。
  7. BZOJ4825: [Hnoi2017]单旋(Splay)
  8. 使用jOOQ的MockDataProvider破解简单的JDBC ResultSet缓存
  9. 冷藏温度范围_食品冷藏冷冻温度要求与管理规范
  10. XSLT2.0---多文件输出
  11. 孙正义在比特币上栽了?传其亏损超过1.3亿美元...
  12. Android,谁动了我的内存
  13. 我一口气发现7个Safari 0day,苹果奖了7.5万美元
  14. iphone11边框喇手问题_iPhone12手感如何 直角边框真的会割手吗?
  15. 科学实证与理论研究方法
  16. html5 立方体 鼠标滑动,,HTML5超酷3D立方体按钮效果
  17. 报表开发工具ActiveReports实战应用教程——分组报表
  18. 腾讯企业邮箱申请步骤
  19. cad隐藏图层命令快捷键_cad解锁图层命令(cad图层全部解锁快捷键)
  20. backbone.js_架构介绍

热门文章

  1. 数据合并处理concat
  2. java中ch或者的符号_java 中文标点符号判断
  3. 鼎信MTG1000 版本升级说明
  4. FPGA零基础学习:基于FPGA的多路选择器设计(附代码)
  5. K-means与GMM分别对球队数据进行聚类
  6. java poi居中_使用apache poi在合并单元格中水平居中图像
  7. 批量给pdf增加水印
  8. vue事件修饰符有哪些
  9. iOS 播放器 或直播添加 弹幕
  10. 大连理工大学计算机专业导师,2017年大连理工大学408计算机学科专业基础综合之计算机操作系统考研导师圈点必考题汇编...