Android实现左滑删除控件

背景:在android开发中,列表是经常会使用到的一个主要控件,列表中可以展示大量的数据,像订单、商品、通讯录、浏览记录或者关注列表等等。可能产品一开始需求只做简单的数据展示,但后期随着功能越来越多,越来越完善,产品可能说在列表里面增加一些交互能力。比如说订单列表里面,一开始只是展示订单数据,后面需要加上删除订单的功能,以前Android中这种功能要的很多的可能就是长按操作这种的,因为程序猿只需要很少的代码就能实现。但是ios的习惯操作是左滑删除,为了保持统一的操作习惯,两端保持一致,最终产品会让Android程序猿去实现一种和ios一模一样的功能。如果你的代码已经维护了很久,代码量比较大,不愿意去大改,那么今天这个控件就能轻松的助你完成左滑删除的功能。

先上效果图:

设计思路:最好以最小的代码侵入来实现左滑删除的功能,在不破坏原来逻辑的基础上,只需稍加改造便可具备左滑删除的能力。

首先分析下左滑删除的基础原理:

原理分析: 

1. 正常状态下,我们看到的是完整的内容部分,右侧菜单部分因为超出屏幕所以不在视线范围内。

2. 手指滑动过程中,容器的内容跟随手指移动,从而拉出在屏幕外面的菜单区域。

3. 当手指松开的时候,我们先假定一种逻辑,如果菜单区域显示超过一半,那就全部显示;如果少于一半那就滑出隐藏。

滑动原理分析完了之后,我们大概就有了实现思路了:

首先我们的控件里面需要两块区域,因为以前可能已经实现了列表item的显示,如果能不做任何改动,直接把以前的item包含到我们的内容区域里面来,那么我们内容区域就轻松搞定了。
菜单区域,需要什么能力,就把相关的View也传递给我容器,然后容器放到相应位置。
谈笑间,简单两步我们的左滑删除容器已经完成一个简单的雏形了!

接下来就是代码实现:

步骤一:内容和菜单分别加入容器

/**
   * 设置内容区域
   * @param contentView
   */
  public void addContentView(View contentView) {
    this.mContentView = contentView;
    this.mContentView.setTag("contentView");

    View cv = findViewWithTag("contentView");
    if (cv != null) {
      this.removeView(cv);
    }
    LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT
    );
    this.addView(this.mContentView, layoutParams);
  }

  /**
   * 设置右边菜单区域
   */
  public void addMenuView(View menuView) {
    this.mMenuView = menuView;
    this.mMenuView.setTag("menuView");

    View mv = findViewWithTag("menuView");
    if (mv != null) {
      this.removeView(mv);
    }
    LayoutParams layoutParams = new LayoutParams(mRightCanSlide, ViewGroup.LayoutParams.MATCH_PARENT);
    this.addView(this.mMenuView, layoutParams);
}

步骤二:左滑处理

/**
   * 拦截触摸事件
   *
   * @param ev
   * @return
   */
  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {

    int actionMasked = ev.getActionMasked();

    Log.e(TAG, "onInterceptTouchEvent: actionMasked = " + actionMasked);

    switch (actionMasked) {
      case MotionEvent.ACTION_DOWN:
        mInitX = ev.getRawX() + getScrollX();
        mInitY = ev.getRawY();
        clearAnim();

        if (mViewPager != null) {
          mViewPager.requestDisallowInterceptTouchEvent(true);
        }

        if (mCardView != null) {
          mCardView.requestDisallowInterceptTouchEvent(true);
        }

        break;

      case MotionEvent.ACTION_MOVE:

        if (mInitX - ev.getRawX() < 0) {

          // 让父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
            isReCompute = false;
          }

          // 阻止ViewPager拦截事件
          if (mViewPager != null) {
            mViewPager.requestDisallowInterceptTouchEvent(true);
          }

          return false;
        }

        // y轴方向上达到滑动最小距离, x 轴未达到
        if (Math.abs(ev.getRawY() - mInitY) >= mTouchSlop
            && Math.abs(ev.getRawY() - mInitY) > Math.abs(mInitX - ev.getRawX() - getScrollX())) {

          // 让父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
            isReCompute = false;
          }

          return false;

        }

        // x轴方向达到了最小滑动距离,y轴未达到
        if (Math.abs(mInitX - ev.getRawX() - getScrollX()) >= mTouchSlop
            && Math.abs(ev.getRawY() - mInitY) <= Math.abs(mInitX - ev.getRawX() - getScrollX())) {

          // 阻止父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(true);
            isReCompute = false;
          }

          return true;
        }

        break;

      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:

        if (mRecyclerView != null) {
          mRecyclerView.requestDisallowInterceptTouchEvent(false);
          isReCompute = true;
        }
        break;
      default:
        break;
    }

    return super.onInterceptTouchEvent(ev);
}
/**
   * 处理触摸事件
   * 需要注意何时处理左滑,何时不处理
   *
   * @param ev
   * @return
   */
  @Override
  public boolean onTouchEvent(MotionEvent ev) {

    int actionMasked = ev.getActionMasked();

    switch (actionMasked) {
      case MotionEvent.ACTION_DOWN:
        mInitX = ev.getRawX() + getScrollX();
        mInitY = ev.getRawY();
        clearAnim();

        if (mViewPager != null) {
          mViewPager.requestDisallowInterceptTouchEvent(true);
        }

        if (mCardView != null) {
          mCardView.requestDisallowInterceptTouchEvent(true);
        }

        break;

      case MotionEvent.ACTION_MOVE:

        if (mInitX - ev.getRawX() < 0) {

          // 让父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
            isReCompute = false;
          }

          // 阻止ViewPager拦截事件
          if (mViewPager != null) {
            mViewPager.requestDisallowInterceptTouchEvent(true);
            isReCompute = false;
          }
        }

        // y轴方向上达到滑动最小距离, x 轴未达到
        if (Math.abs(ev.getRawY() - mInitY) >= mTouchSlop
            && Math.abs(ev.getRawY() - mInitY) > Math.abs(mInitX - ev.getRawX() - getScrollX())) {

          // 让父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
            isReCompute = false;
          }
        }

        // x轴方向达到了最小滑动距离,y轴未达到
        if (Math.abs(mInitX - ev.getRawX() - getScrollX()) >= mTouchSlop
            && Math.abs(ev.getRawY() - mInitY) <= Math.abs(mInitX - ev.getRawX() - getScrollX())) {

          // 阻止父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(true);
            isReCompute = false;
          }
        }

        /** 如果手指移动距离超过最小距离 */
        float translationX = mInitX - ev.getRawX();

        // 如果滑动距离已经大于右边可伸缩的距离后, 应该重新设置initx
        if (translationX > mRightCanSlide) {
          mInitX = ev.getRawX() + mRightCanSlide;

        }

        // 如果互动距离小于0,那么重新设置初始位置initx
        if (translationX < 0) {
          mInitX = ev.getRawX();
        }

        translationX = translationX > mRightCanSlide ? mRightCanSlide : translationX;
        translationX = translationX < 0 ? 0 : translationX;

        // 向左滑动
        if (translationX <= mRightCanSlide && translationX >= 0) {

          scrollTo((int) translationX, 0);

          return true;
        }

        break;

      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:

        if (mRecyclerView != null) {
          mRecyclerView.requestDisallowInterceptTouchEvent(false);
          isReCompute = true;
        }

        upAnim();

        return true;

        default:
          break;
    }

    return true;
}

以上两个方法主要处理了左滑移动功能以及滑动冲突问题,如果用的是RecyclerView那么为了防止垂直方向的同向冲突,那么需要将外层的RecyclerView传入左滑容器,在这个容器中会处理滑动冲突。

到这就已经实现了左滑功能,并且解决掉了垂直方向上的滑动冲突,然后我们还要实现一个功能是:如果有一个item向左滑动并显示出右边的菜单区域,当手指再次按下或者列表滑动的时候,需要将已经显示菜单区域的item收起,恢复原来的状态。为了提供这个能力,左滑容器里面提供一个菜单状态变化的监听:

/**
   * 删除按钮状态变化监听
   */
  public interface OnDelViewStatusChangeLister {

    /**
     * 状态变化监听
     * @param show 是否正在显示
     */
    void onStatusChange(boolean show);
  }

/**
   * 重置 菜单展开/菜单收起 状态
   */
  public void resetDelStatus() {

    int scrollX = getScrollX();

    if (scrollX == 0) {
      return;
    }

    clearAnim();

    mValueAnimator = ValueAnimator.ofInt(scrollX, 0);
    mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        int value = (int) animation.getAnimatedValue();

        scrollTo(value, 0);
      }
    });

    mValueAnimator.setDuration(mAnimDuring);
    mValueAnimator.start();
  }

菜单展开或者收起都会调用这个方法,方便第三方调用者处理状态。

再者还有就是加上动画,让滑动更加柔和:

/**
   * 手指抬起执行动画
   */
  private void upAnim() {
    int scrollX = getScrollX();

    if (scrollX == mRightCanSlide || scrollX == 0) {

      if (mStatusChangeLister != null) {
        mStatusChangeLister.onStatusChange(scrollX == mRightCanSlide);
      }

      return;
    }

    clearAnim();

    // 如果显出一半松开手指,那么自动完全显示。否则完全隐藏
    if (scrollX >= mRightCanSlide / 2) {
      mValueAnimator = ValueAnimator.ofInt(scrollX, mRightCanSlide);
      mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          int value = (int) animation.getAnimatedValue();

          scrollTo(value, 0);
        }
      });

      mValueAnimator.setDuration(mAnimDuring);
      mValueAnimator.start();

      if (mStatusChangeLister != null) {
        mStatusChangeLister.onStatusChange(true);
      }
    }
    else {
      mValueAnimator = ValueAnimator.ofInt(scrollX, 0);
      mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          int value = (int) animation.getAnimatedValue();

          scrollTo(value, 0);
        }
      });

      mValueAnimator.setDuration(mAnimDuring);
      mValueAnimator.start();

      if (mStatusChangeLister != null) {
        mStatusChangeLister.onStatusChange(false);
      }
    }
  }

#最后贴上左滑删除容器的完整代码:

/**
 * @author luowang
 * @date 2020-08-19 17:31
 * 左滑删除View
 */
public class LeftSlideView extends LinearLayout {

  /**
   * tag
   */
  public static final String TAG = "LeftSlideView";

  /**
   * 上下文
   */
  private Context mContext;

  /**
   * 最小触摸距离
   */
  private int mTouchSlop;

  /**
   * 右边可滑动距离
   */
  private int mRightCanSlide;

  /**
   * 按下x
   */
  private float mInitX;

  /**
   * 按下y
   */
  private float mInitY;

  /**
   * 属性动画
   */
  private ValueAnimator mValueAnimator;

  /**
   * 动画时长
   */
  private int mAnimDuring = 200;

  /**
   * 删除按钮的长度
   */
  private int mDelLength = 76;

  /**
   * ViewPager
   */
  private ViewPager mViewPager;

  /**
   * RecyclerView
   */
  private RecyclerView mRecyclerView;

  /** CardView */
  private CardView mCardView;

  /** 是否重新计算 */
  private boolean isReCompute = true;

  /** 状态监听 */
  private OnDelViewStatusChangeLister mStatusChangeLister;

  /**
   * 内容区域View
   */
  private View mContentView;

  /**
   * 菜单区域View
   */
  private View mMenuView;

  public LeftSlideView(Context context) {
    this(context, null);
  }

  public LeftSlideView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public LeftSlideView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.mContext = context;

    init();
  }

  /**
   * 初始化
   */
  private void init() {
    mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
    mRightCanSlide = DPIUtil.dip2px(mContext, mDelLength);
    setBackgroundColor(Color.TRANSPARENT);
    // 水平布局
    setOrientation(LinearLayout.HORIZONTAL);
    initView();
  }

  /**
   * 设置内容区域
   * @param contentView
   */
  public void addContentView(View contentView) {
    this.mContentView = contentView;
    this.mContentView.setTag("contentView");

    View cv = findViewWithTag("contentView");
    if (cv != null) {
      this.removeView(cv);
    }
    LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT
    );
    this.addView(this.mContentView, layoutParams);
  }

  /**
   * 设置右边菜单区域
   */
  public void addMenuView(View menuView) {
    this.mMenuView = menuView;
    this.mMenuView.setTag("menuView");

    View mv = findViewWithTag("menuView");
    if (mv != null) {
      this.removeView(mv);
    }
    LayoutParams layoutParams = new LayoutParams(mRightCanSlide, ViewGroup.LayoutParams.MATCH_PARENT);
    this.addView(this.mMenuView, layoutParams);
  }

  /**
   * 设置Viewpager
   */
  public void setViewPager(ViewPager viewPager) {
    mViewPager = viewPager;
  }

  /**
   * 设置RecyclerView
   */
  public void setRecyclerView(RecyclerView recyclerView) {
    mRecyclerView = recyclerView;
  }

  /** 设置CardView */
  public void setCardView(CardView cardView) {
    mCardView = cardView;
  }

  /** 设置状态监听 */
  public void setStatusChangeLister(OnDelViewStatusChangeLister statusChangeLister) {
    mStatusChangeLister = statusChangeLister;
  }

  /**
   * 初始化View
   */
  private void initView() {

  }

  /**
   * 拦截触摸事件
   *
   * @param ev
   * @return
   */
  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {

    int actionMasked = ev.getActionMasked();

    Log.e(TAG, "onInterceptTouchEvent: actionMasked = " + actionMasked);

    switch (actionMasked) {
      case MotionEvent.ACTION_DOWN:
        mInitX = ev.getRawX() + getScrollX();
        mInitY = ev.getRawY();
        clearAnim();

        if (mViewPager != null) {
          mViewPager.requestDisallowInterceptTouchEvent(true);
        }

        if (mCardView != null) {
          mCardView.requestDisallowInterceptTouchEvent(true);
        }

        break;

      case MotionEvent.ACTION_MOVE:

        if (mInitX - ev.getRawX() < 0) {

          // 让父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
            isReCompute = false;
          }

          // 阻止ViewPager拦截事件
          if (mViewPager != null) {
            mViewPager.requestDisallowInterceptTouchEvent(true);
          }

          return false;
        }

        // y轴方向上达到滑动最小距离, x 轴未达到
        if (Math.abs(ev.getRawY() - mInitY) >= mTouchSlop
            && Math.abs(ev.getRawY() - mInitY) > Math.abs(mInitX - ev.getRawX() - getScrollX())) {

          // 让父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
            isReCompute = false;
          }

          return false;

        }

        // x轴方向达到了最小滑动距离,y轴未达到
        if (Math.abs(mInitX - ev.getRawX() - getScrollX()) >= mTouchSlop
            && Math.abs(ev.getRawY() - mInitY) <= Math.abs(mInitX - ev.getRawX() - getScrollX())) {

          // 阻止父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(true);
            isReCompute = false;
          }

          return true;
        }

        break;

      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:

        if (mRecyclerView != null) {
          mRecyclerView.requestDisallowInterceptTouchEvent(false);
          isReCompute = true;
        }
        break;
      default:
        break;
    }

    return super.onInterceptTouchEvent(ev);
  }

  /**
   * 处理触摸事件
   * 需要注意何时处理左滑,何时不处理
   *
   * @param ev
   * @return
   */
  @Override
  public boolean onTouchEvent(MotionEvent ev) {

    int actionMasked = ev.getActionMasked();

    switch (actionMasked) {
      case MotionEvent.ACTION_DOWN:
        mInitX = ev.getRawX() + getScrollX();
        mInitY = ev.getRawY();
        clearAnim();

        if (mViewPager != null) {
          mViewPager.requestDisallowInterceptTouchEvent(true);
        }

        if (mCardView != null) {
          mCardView.requestDisallowInterceptTouchEvent(true);
        }

        break;

      case MotionEvent.ACTION_MOVE:

        if (mInitX - ev.getRawX() < 0) {

          // 让父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
            isReCompute = false;
          }

          // 阻止ViewPager拦截事件
          if (mViewPager != null) {
            mViewPager.requestDisallowInterceptTouchEvent(true);
            isReCompute = false;
          }
        }

        // y轴方向上达到滑动最小距离, x 轴未达到
        if (Math.abs(ev.getRawY() - mInitY) >= mTouchSlop
            && Math.abs(ev.getRawY() - mInitY) > Math.abs(mInitX - ev.getRawX() - getScrollX())) {

          // 让父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
            isReCompute = false;
          }
        }

        // x轴方向达到了最小滑动距离,y轴未达到
        if (Math.abs(mInitX - ev.getRawX() - getScrollX()) >= mTouchSlop
            && Math.abs(ev.getRawY() - mInitY) <= Math.abs(mInitX - ev.getRawX() - getScrollX())) {

          // 阻止父级容器拦截
          if (mRecyclerView != null && isReCompute) {
            mRecyclerView.requestDisallowInterceptTouchEvent(true);
            isReCompute = false;
          }
        }

        /** 如果手指移动距离超过最小距离 */
        float translationX = mInitX - ev.getRawX();

        // 如果滑动距离已经大于右边可伸缩的距离后, 应该重新设置initx
        if (translationX > mRightCanSlide) {
          mInitX = ev.getRawX() + mRightCanSlide;

        }

        // 如果互动距离小于0,那么重新设置初始位置initx
        if (translationX < 0) {
          mInitX = ev.getRawX();
        }

        translationX = translationX > mRightCanSlide ? mRightCanSlide : translationX;
        translationX = translationX < 0 ? 0 : translationX;

        // 向左滑动
        if (translationX <= mRightCanSlide && translationX >= 0) {

          scrollTo((int) translationX, 0);

          return true;
        }

        break;

      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:

        if (mRecyclerView != null) {
          mRecyclerView.requestDisallowInterceptTouchEvent(false);
          isReCompute = true;
        }

        upAnim();

        return true;

        default:
          break;
    }

    return true;
  }

  /**
   * 清除动画
   */
  private void clearAnim() {
    if (mValueAnimator == null) {
      return;
    }

    mValueAnimator.end();
    mValueAnimator.cancel();
    mValueAnimator = null;
  }

  /**
   * 手指抬起执行动画
   */
  private void upAnim() {
    int scrollX = getScrollX();

    if (scrollX == mRightCanSlide || scrollX == 0) {

      if (mStatusChangeLister != null) {
        mStatusChangeLister.onStatusChange(scrollX == mRightCanSlide);
      }

      return;
    }

    clearAnim();

    // 如果显出一半松开手指,那么自动完全显示。否则完全隐藏
    if (scrollX >= mRightCanSlide / 2) {
      mValueAnimator = ValueAnimator.ofInt(scrollX, mRightCanSlide);
      mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          int value = (int) animation.getAnimatedValue();

          scrollTo(value, 0);
        }
      });

      mValueAnimator.setDuration(mAnimDuring);
      mValueAnimator.start();

      if (mStatusChangeLister != null) {
        mStatusChangeLister.onStatusChange(true);
      }
    }
    else {
      mValueAnimator = ValueAnimator.ofInt(scrollX, 0);
      mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          int value = (int) animation.getAnimatedValue();

          scrollTo(value, 0);
        }
      });

      mValueAnimator.setDuration(mAnimDuring);
      mValueAnimator.start();

      if (mStatusChangeLister != null) {
        mStatusChangeLister.onStatusChange(false);
      }
    }
  }

  /**
   * 重置
   */
  public void resetDelStatus() {

    int scrollX = getScrollX();

    if (scrollX == 0) {
      return;
    }

    clearAnim();

    mValueAnimator = ValueAnimator.ofInt(scrollX, 0);
    mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        int value = (int) animation.getAnimatedValue();

        scrollTo(value, 0);
      }
    });

    mValueAnimator.setDuration(mAnimDuring);
    mValueAnimator.start();
  }

  /**
   * 删除按钮状态变化监听
   */
  public interface OnDelViewStatusChangeLister {

    /**
     * 状态变化监听
     * @param show 是否正在显示
     */
    void onStatusChange(boolean show);
  }

}

完整DEMO直通车

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android仿QQ左滑删除置顶ListView操作

    最近闲来无事,于是研究了一下qq的左滑删除效果,尝试着实现了一下,先上效果图: 大致思路原理: - 通过设置margin实现菜单的显示与隐藏 - 监听onTouchEvent,处理滑动事件 上代码 import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.v

  • Android ListView实现仿iPhone实现左滑删除按钮的简单实例

    需要自定义ListView.这里就交FloatDelListView吧. 复写onTouchEvent方法.如下: @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN:<BR> // 获取按下的条目视图(child view) int childCount = getChildCount(); int[] listViewCo

  • Android使用CardView作为RecyclerView的Item并实现拖拽和左滑删除

    引言 CardView是Android 5.0系统之后引入的众多控件之一,实现之后的效果也是比较酷的,它经常被用在RecyclerView和ListView中的Item中.今天我们就来了解一下CardView的属性,然后使用CardView和RecyclerView结合实现一个可以拖拽Item的布局. CardView的属性 CardView继承自FrameLayout,所以子控件的布局规则和FrameLayout的一样,是按照层次堆叠的 下面是CardView的一些常用属性: CardView

  • Android自定义组合控件之自定义下拉刷新和左滑删除实例代码

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

  • Android使用PullToRefresh完成ListView下拉刷新和左滑删除功能

    ListView下刷新刷功能相信从事Android开发的猿友们并不陌生,包括现在Google亲儿子SwipeRefreshLayout实现效果在一些APP上也能看见(不过个人不喜欢官方的刷新效果).本文就带领一些刚入门android的朋友或者一起爱分享的朋友来简单的实现ListView的下拉刷新和左滑删除效果. 一.本文主要内容: 使用PullToRefresh完成ListView下拉.上拉刷新: 扩展PullToRefresh完美的实现ListView左滑删除效果: 注意:本文中的PullTo

  • Android仿QQ列表左滑删除操作

    最近学习了如何做一个像QQ的左滑RecyclerView的item显示选项的,主要是用到Scroller 我们首先新建一个自己的RecyclerView 定义好一些要用的的变量 重写构造方法,把前两个构造方法改为如下,使无论如何构造都要执行第三个构造方法 在第三个构造方法里初始化Scroller public class LeftSwipeMenuRecyclerView extends RecyclerView { //置顶按钮 private TextView tvTop; //删除按钮 p

  • Android实现左滑删除列表功能

    本文实例为大家分享了Android自定义左滑删除列表的具体代码,供大家参考,具体内容如下 1.布局文件view_left_slide_remove.xml 包含一个RelativeLayout和TextView,RelativeLayout是实际界面,TextView是删除按钮. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://sch

  • android实现简单左滑删除控件

    本文为大家分享了一个简单的android左滑删除控件,供大家参考,具体内容如下 import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PointF; import android.support.v4.view.ViewConfigurationCompat; import android.util.AttributeSet; import android.v

  • Android实现左滑删除控件

    背景:在android开发中,列表是经常会使用到的一个主要控件,列表中可以展示大量的数据,像订单.商品.通讯录.浏览记录或者关注列表等等.可能产品一开始需求只做简单的数据展示,但后期随着功能越来越多,越来越完善,产品可能说在列表里面增加一些交互能力.比如说订单列表里面,一开始只是展示订单数据,后面需要加上删除订单的功能,以前Android中这种功能要的很多的可能就是长按操作这种的,因为程序猿只需要很少的代码就能实现.但是ios的习惯操作是左滑删除,为了保持统一的操作习惯,两端保持一致,最终产品会

  • Android自定义view实现列表内左滑删除Item

    目录 前言 需求 运行效果 编写代码 主要问题 动态生成TextView 将TextView对齐到当前容器右端 滑动出界问题 滑动开始判定 后续订正 前言 上一篇文章自定义了一个左滑删除的RecyclerView,把view事件分发三个函数dispatchTouchEvent.onInterceptTouchEvent.onTouchEvent实际运用了一下,一些原理通过出现的bug还是挺能加深印象,并且后面还在优化上用上了TouchSlop.VelocityTracker以及GestureDe

  • Android 实现左滑出现删除选项

    滑动删除的部分主要包含两个部分, 一个是内容区域(用于放置正常显示的view),另一个是操作区域(用于放置删除按钮).默认情况下,操作区域是不显示的,内容区域的大小是填充整个容 器,操作区域始终位于内容区域的右面.当开始滑动的时候,整个容器中的所有子view都像左滑动,如果操作区域此时是不可见的,设置为可见. 实现思路就是自定义一个layout SwipeLayout继承自FrameLayout.SwipeLayout包含两个子view,第一个子view是内容区域,第二个子view是操作 区域.

  • AngularJS仿苹果滑屏删除控件

    AngularJs被用来开发单页面应用程序(SPA),利用AJAX调用配合页面的局部刷新,可以减少页面跳转,从而获得更好的用户体验.Angular的ngView及其对应的强大路由机制,是实现SPA应用的核心模块.本文所说的页面切换指的就是这个路由机制,即根据不同的url展示不同的视图. 前端开发中,为了对列表项进行快捷操作,有时就添个按钮来简单实现.但是,有时会发现按钮影响美观,甚至影响列表行的布局.稍在网上搜索无果,而写此仿苹果滑屏删除控件. 依赖项:angularJS.jQuery 测试浏览

随机推荐