Android仿微信列表滑动删除 如何实现滑动列表SwipeListView

接上一篇,本篇主要讲如何实现滑动列表SwipeListView。

上篇完成了滑动控件SwipeItemView,这个控件是一个自定义的ViewGroup,作为列表的一个item,为列表提供一些方法让这个SwipeItemView能滑动其视图内容,同时滑动过程中会有顺滑的动画效果。而本篇讲的SwipeListView则是这个列表的具体实现了。当然啦,这个SwipeListView继承自ListView,为了实现我们需要的功能,重点就是重写ListView的onTouchEvent()以及onInterceptTouchEvent()这个方法了。先说onTouchEvent():

@Override
  public boolean onTouchEvent(MotionEvent ev) {

    //if user had not set mSwipeItemViewID, not handle any touch event
    if(mSwipeItemViewID == -1)
      return super.onTouchEvent(ev);

    if(mCancelMotionEvent && ev.getAction() == MotionEvent.ACTION_DOWN) {
      //ev.setAction(MotionEvent.ACTION_CANCEL);
      LogUtil.Log("SwipeListView.onTouchEvent(), cancel ACTION_DOWN");
      hideShowingItem();

      return true;
    } else if(mCancelMotionEvent && ev.getAction() == MotionEvent.ACTION_MOVE) {
      if(mSwipeItemView.getCurrentScrollX() > 0) {
        mSwipeItemView.computeScroll();
        //mSwipeItemView.scrollBy(-1, 0);
      }

      return true;
    } else if(mCancelMotionEvent && ev.getAction() == MotionEvent.ACTION_UP) {
      mCancelMotionEvent = false;

      return true;
    }

    switch(ev.getAction()) {
      case MotionEvent.ACTION_DOWN: {
        LogUtil.Log("ACTION_DOWN");
        if(mTracker == null) {
          mTracker = VelocityTracker.obtain();
        } else {
          mTracker.clear();
        }

        mActionDownX = ev.getX();
        mActionDownY = ev.getY();
        mLastMotionX = ev.getX();
        mLastMotionY = ev.getY();
      }break;

      case MotionEvent.ACTION_MOVE: {
        //if the scroll distance at X-axis or Y-axis less than the
        //TOUCH_SLOP, do not handle the event MotionEvent.ACTION_MOVE
        if(Math.abs(ev.getX() - mActionDownX) < TOUCH_SLOP
            || Math.abs(ev.getY() - mActionDownY) < TOUCH_SLOP)
          break;

        float curX = ev.getX();
        float curY = ev.getY();
        int distanceX = (int)(mLastMotionX - curX);
        int distanceY = (int)(mLastMotionY - curY);

        if(mScrollDirection == DIRECTION_UNKNOW
            && Math.abs(distanceY) <= Math.abs(distanceX))
          mScrollDirection = DIRECTION_HORIZONTAL;
        else if(mScrollDirection == DIRECTION_UNKNOW
            && Math.abs(distanceY) > Math.abs(distanceX))
          mScrollDirection = DIRECTION_VERTICAL;

        //if ListView is scrolling vertical, do not handle the touch event
        if(mScrollDirection == DIRECTION_VERTICAL)
          break;

        int lastPos = pointToPosition((int)mActionDownX, (int)mActionDownY);
        int firstVisibleItemPos = getFirstVisiblePosition()
            - getHeaderViewsCount();
        int factPos = lastPos - firstVisibleItemPos;
        mItemView = getChildAt(factPos);
        if(mItemView != null) {
          mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID);
          if(mSwipeItemView.getSlidingView() != null
              && mSwipeItemView.getScrollX()
                  <= mSwipeItemView.getSlidingView().getWidth()
              && mSwipeItemView.getScrollX() >= 0) {
            if(mSwipeItemView.getScrollX() + distanceX
                > mSwipeItemView.getSlidingView().getWidth())
              distanceX = mSwipeItemView.getSlidingView().getWidth()
                  - mSwipeItemView.getScrollX();
            else if(mSwipeItemView.getScrollX() + distanceX < 0)
              distanceX = -mSwipeItemView.getScrollX();

            mSwipeItemView.scrollBy(distanceX, 0);
          }

          mLastShowingPos = lastPos;

          ev.setAction(MotionEvent.ACTION_CANCEL);
        }

        mLastMotionX = curX;
        mLastMotionY = curY;
      }break;

      case MotionEvent.ACTION_UP: {
        LogUtil.Log("ACTION_UP");
        if(mTracker != null) {
          mTracker.clear();
          mTracker.recycle();
          mTracker = null;
        }

        //reset the mScrollDirection to DIRECTION_UNKNOW
        mScrollDirection = DIRECTION_UNKNOW;

        //reset the mCancelMotionEvent to false
        mCancelMotionEvent = false;

        //ensure if the showing item need open or hide
        if(mLastShowingPos != -1)
          ensureIfItemOpenOrHide();
      }break;

      case MotionEvent.ACTION_CANCEL: {
        hideShowingItem();
      }break;
    }

    return super.onTouchEvent(ev);
  }

上面代码,首先分析用户滑开一个item的操作,这个操作以ACTION_DOWN起始,一系列的ACTION_MOVE,以ACTION_UP作为结束,所以,在ACTION_DOWN事件里面,我们先记录下最开始的事件位置mActionDownX和mActionDownY;然后再ACTION_MOVE事件里面,我们先要进行判断,这个判断分两步,第一步,判断这个ACTION_MOVE事件下,当前事件的位置curX和curY在x轴上以及y轴上和ACTION_DOWN里面记录的位置的距离是否已经超过TOUCH_SLOP的值,这个值是android用来判断是否应该进行一次滑动的阈值,第二步,我们要进一步判断用户是纵向滑动这整个列表还是左右滑动某个item,这里的逻辑判断就简单点处理,若是超过TOUCH_SLOP阈值的情况下,x轴方向上距离大于y轴的,我们就认为用户是左右滑动单个item,反之则是纵向滑动整个列表,这里我们用三种状态区分,DIRECTION_UNKNOW表示当前的滑动操作还没有进行判断左右滑动还是纵向滑动,DIRECTION_HORIZONTAL表示当前滑动操作判定为左右滑动,DIRECTION_VERTICAL表示判定为纵向滑动,一旦滑动操作被判定了,则在ACTION_UP处理前,我们都认为用户是做同一方向的滑动;ACTION_UP事件里面,重置滑动操作状态为DIRECTION_UNKNOW以便下一次的判定,以及这次ACTION_UP事件处理的时候,如果当前滑开的item的位置mLastShowingPos不为-1,则表示当前是一次滑开的操作,这次仔细想想,用户可能在并没有完全滑开这个item的状态下手就离开屏幕了,这时候我们就应该要判断此时这个被滑动的item是应该完全打开又或者是关闭,这里的逻辑判断是item已经滑开的距离超过它本身宽度的一半的话,就完全打开它,否则就关闭它,ensureIfItemOpenOrHide()的具体代码如下:

 private void ensureIfItemOpenOrHide() {
    if(mLastShowingPos != -1) {
      int firstVisibleItemPos = getFirstVisiblePosition()
          - getHeaderViewsCount();
      int factPos = mLastShowingPos - firstVisibleItemPos;
      mItemView = getChildAt(factPos);
      if(mItemView != null) {
        mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID);
        if(mSwipeItemView.getSlidingView() != null &&
            mSwipeItemView.getScrollX() >=
                mSwipeItemView.getSlidingView().getWidth() / 2) {
          openShowingItem();
        } else if(mSwipeItemView.getSlidingView() != null) {
          hideShowingItem();
        }
      }
    }
  }

那第一次的用户滑动操作的逻辑判定我们就算处理完了。接下来是第二次的,为什么说第二次呢,第一次用户滑开了某单个的item,使其处于打开的状态下,再一次触摸屏幕,这次我们则要再一次进行判定,其一,如果ACTION_DOWN发生的位置在item滑开显示出来的button的范围内,表示当前用户是点击这个button,这样我们就不做额外处理,直接交由列表默认的逻辑处理;其二,如果ACTION_DOWN发生的位置不在item滑开后显示出来的button范围内,怎表示当前用户只是操作列表的其他范围,这里我们就关闭当前打开了的item,并取消后续的touch事件,这里的话,我们就要截获这个ACTIOIN_DOWN事件了,需要重写ListView的onInterceptTouchEvent()方法,代码如下:

public boolean onInterceptTouchEvent(MotionEvent ev) {
    //if user had not set mSwipeItemViewID, not handle any touch event
    if(mSwipeItemViewID == -1)
      return super.onInterceptTouchEvent(ev);

    if(mLastShowingPos != -1
        && ev.getAction() == MotionEvent.ACTION_DOWN
        && !isClickChildView(ev)) {
      LogUtil.Log("SwipeListView.onInterceptTouchEvent(), intercept ACTION_DOWN");
      mCancelMotionEvent = true;

      return true;
    } else if(mLastShowingPos == -1
        && ev.getAction() == MotionEvent.ACTION_DOWN) {
      return true;
    }

    return super.onInterceptTouchEvent(ev);
  }

上面的mCancelMotionEvent是用来在onTouchEvent()里面判断是否需要取消后续touch事件的标志,那期间,如何判断当前的ACTION_DOWN事件是否发生在button的范围内呢,使用如下代码:

private boolean isClickChildView(MotionEvent event) {
    if(mLastShowingPos != -1) {
      int firstVisibleItemPos = getFirstVisiblePosition()
          - getHeaderViewsCount();
      int factPos = mLastShowingPos - firstVisibleItemPos;
      mItemView = getChildAt(factPos);
      if(mItemView != null) {
        mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID);
        View slidingView = mSwipeItemView.getSlidingView();
        if(slidingView != null) {
          int[] slidingViewLocation = new int[2];
          slidingView.getLocationOnScreen(slidingViewLocation);

          int left = slidingViewLocation[0];
          int right = slidingViewLocation[0] + slidingView.getWidth();
          int top = slidingViewLocation[1];
          int bottom = slidingViewLocation[1] + slidingView.getHeight();

          return (event.getRawX() > left && event.getRawX() < right
              && event.getRawY() > top && event.getRawY() < bottom);
        }
      }
    }

    return false;
  }

剩下的,就是如何打开或者关闭一个item了,代码如下:

private void openShowingItem() {
    if(mLastShowingPos != -1) {
      int firstVisibleItemPos = getFirstVisiblePosition()
          - getHeaderViewsCount();
      int factPos = mLastShowingPos - firstVisibleItemPos;
      mItemView = getChildAt(factPos);
      if(mItemView != null) {
        mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID);
        if(mSwipeItemView.getSlidingView() != null)
          mSwipeItemView.scrollToWithAnimation(
              mSwipeItemView.getSlidingView().getWidth(), 0);
      }
    }
  }
   private void hideShowingItem() {
    if(mLastShowingPos != -1) {
      int firstVisibleItemPos = getFirstVisiblePosition()
          - getHeaderViewsCount();
      int factPos = mLastShowingPos - firstVisibleItemPos;
      mItemView = getChildAt(factPos);
      if(mItemView != null) {
        mSwipeItemView = (SwipeItemView)mItemView.findViewById(mSwipeItemViewID);
        mSwipeItemView.scrollToWithAnimation(0, 0);
      }

      mLastShowingPos = -1;
    }
  }

上面的scrollToWithAnimation()方法就是上一篇博客里面我们实现了的移动item并使其带有动画效果的方法了。

这样,整个仿微信滑动删除操作的总体实现方案就解释完毕了,具体一些细节的话可以查看这个工程的源码,源码我已经上传到了我的Github主页上:https://github.com/YoungLeeForeverBoy/SlidingListViewPlus

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

(0)

相关推荐

  • Android仿微信列表滑动删除之可滑动控件(一)

    这次是列表滑动删除的第三波,仿微信的列表滑动删除.先上个效果图: 前面的文章里面说过开源框架SwipeListView的实现原理是每个列表item中包含上下两层view,普通状态下上层的view覆盖着下层的view,当用户滑开上层的view,下层的view就显示出来了.但是仔细观察微信列表的item,很明显并非这个实现方案,微信的item应该一个单层view,只不过这个item超出了所在的ListView的宽度,在用户滑动item的时候,item超出屏幕的view则会显示在屏幕之上,这种滑动实现

  • Android实现仿微信tab高亮icon粘着手的滑动效果

    微信的主页分为3个tab,被选中的tab的tabwidget下面会有一个高亮的长条icon,而当切换tab页面的时候,这个icon不是等到tab切换完成后再出现在当前被选中的tab的tabwidget的下面,而是会随着viewpager滑动页面的动作也进行滑动,从前一个tabwidget滑到当前被选中的tabwidget,像viewpager一样有一种粘着你的手的效果,体验很赞.上个图: 本篇分析如何实现这个效果. 首先基本知识是,实现不同tab页之间可以滑动切换需要用到TabPageIndic

  • Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能

    如何为不同的list item呈现不同的菜单,本文实例就为大家介绍了Android仿微信或QQ滑动弹出编辑.删除菜单效果.增加下拉刷新等功能的实现,分享给大家供大家参考,具体内容如下 效果图: 1. 下载开源项目,并将其中的liberary导入到自己的项目中: 2. 使用SwipeMenuListView代替ListView,在页面中布局: <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeRefresh

  • Android 高仿微信朋友圈动态支持双击手势放大并滑动查看图片效果

    最近参与了开发一款旅行APP,其中包含实时聊天和动态评论功能,终于耗时几个月几个伙伴完成了,今天就小结一下至于实时聊天功能如果用户不多的情况可以scoket实现,如果用户万级就可以采用开源的smack + opnefile实现,也可以用mina开源+XMMP,至于怎么搭建和实现,估计目前github上一搜一大把,至于即时通讯怕误人子弟,暂且不做介绍,现就把实现的一个微信朋友圈的小功能介绍一下. 先上效果图: 一拿到主流的UI需求,大致分析下,需要我ListView嵌套Gridview,而grid

  • Android仿微信右滑返回功能的实例代码

    先上效果图,如下: 先分析一下功能的主要技术点,右滑即手势判断,当滑到一直距离时才执行返回,并且手指按下的位置是在屏幕的最左边(这个也是有一定范围的),  这些可以实现onTouchEvent来实现. 接着就是返回时,有滑动效果,很显然这个是Acitivty切换动画实现的.好啦,分析完了就开干.下面上代码: @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case Mot

  • Android高仿微信对话列表滑动删除效果

    前言 用过微信的都知道,微信对话列表滑动删除效果是很不错的,这个效果我们也可以有.思路其实很简单,弄个ListView,然后里面的每个item做成一个可以滑动的自定义控件即可.由于ListView是上下滑动而item是左右滑动,因此会有滑动冲突,也许你需要了解下android中点击事件的派发流程,请参考Android源码分析-点击事件派发机制.我的解决思路是这样的:重写ListView的onInterceptTouchEvent方法,在move的时候做判断,如果是左右滑动就返回false,否则返

  • Android实现微信首页左右滑动切换效果

    大家看到微信首页切换效果有没有觉得很炫,滑动切换,点击底部bar瞬间切换,滑动切换渐变效果,线上效果图: 之前也在博客上看到别人的实现,再次基础上,我做了些优化.首先说下实现原理,大神略过,o(╯□╰)o 页面上看到的三个页面是三个Fragment, 左右滑动使用viewpager,相信大家也都是这么再用,那么底部用的是什么技术呢,底部渐变其实就是重写了ImageView,以及在左右滑动时,改变了TextView的颜色值,是不是很简单...下面我们一步一步的来: 1.自定义ImageView:

  • Android仿微信滑动退出Activity

    效果图: 原理: 原理一句话就能描述清楚.重写Activity的dispatchTouchEvent,滑动的时候拿到Activity栈中栈顶Activity的上一个Acticity的ContentView添加到栈顶Activity的DecorView中,滑动的过程中做视图平移,滑动结束之后把前面拿过来用的ContentView归还给上一个Activity,然后finish当前Activity. ActivityStack: 实现 Application.ActivityLifecycleCall

  • Android仿微信通讯录列表侧边栏效果

    先看Android仿微信通讯录列表侧边栏效果图 这是比较常见的效果了吧 列表根据首字符的拼音字母来排序,且可以通过侧边栏的字母索引来进行定位. 实现这样一个效果并不难,只要自定义一个索引View,然后引入一个可以对汉字进行拼音解析的jar包--pinyin4j-2.5.0即可 首先,先来定义侧边栏控件View,只要直接画出来即可. 字母选中项会变为红色,且滑动时背景会变色,此时SideBar并不包含居中的提示文本 public class SideBar extends View { priva

  • Android仿微信@好友功能 输入@跳转、删除整块

    最近在做聊天功能的时候,有一个需求是仿照微信做@好友的功能,本来以为挺简单,但是做到这块的时候,发现和想象的有点不一样,什么整块删除,块可编辑,总之,加个@的功能很简单,但是要做和微信的一样还是费了一些功夫,下面是一个demo仅供参考,防止遗忘 先上个效果图 就是这么个功能 1. 分析需求 输入@跳转到联系人界面,选中一个或者多个好友返回到当前界面 按退格键删除整块内容 块内的内容可编辑,编辑完了之后将不附带@功能,只是单纯的文字 2. 开始编码 既然是文本输入首先继承EditText自定义一个

  • Android仿微信实现左滑显示删除按钮功能

    在实际项目中删除列表中的某一项是非常常见的功能,传统的做法可以使用长按监听器等,而现在流行的做法是左滑弹出删除按钮,微信,QQ等都是这么做的,下面做一个示例,代码如下: 主页面MainActivity:代码比较简单常规 package com.home.testslideview; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.os.Bundle; im

  • Android仿微信左右滑动点击切换页面和图标

    本文实例为大家分享了Android仿微信左右滑动点击切换页面和图标的具体代码,供大家参考,具体内容如下 目标效果: 使用鼠标滑动屏幕或者点击下边的小图标,可以更改页面和图标,因为没有那么多素材所以只用了两张图片区分. 1.layout文件夹下新建top.xml页面,作为顶部标题. top.xml页面: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="h

  • android仿微信好友列表功能

    android studio实现微信好友列表功能,注意有一个jar包我没有放上来,请大家到MainActivity中的那个网址里面下载即可,然后把pinyin4j-2.5.0.jar复制粘贴到项目的app/libs文件夹里面,然后clean项目就可以使用了 实现效果图: (1)在build.gradle中引用第三方的类库 compile 'com.android.support:recyclerview-v7:26.0.0-alpha1' compile files('libs/pinyin4j

  • Android 仿微信发动态九宫格拖拽、删除功能

    1.完美1比1 仿照微信仿微信发动态 九宫格拖拽.删除 暴力拖拽ui有点问题,不影响使用,资源文件自己找个+号 2.微信发动态拖拽bug 当选择完图片,长按图片拖拽过程中按下屏幕home键盘,再次进入这时候就不能点击输入文字,点击输入文字的时候会触发选择相册事件 3.拖拽事件用的basequickadapter implementation 'com.android.support:recyclerview-v7:28.0.0' implementation "com.github.CymCha

  • Android实现简单底部导航栏 Android仿微信滑动切换效果

    Android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义View配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏的设置方法类似于TabLayout的关联,View需要创建关联方法,用来关联VIewPager; 3. 通过关联方法获取ViewPager实例后,根据ViewPager页面数创建底部导航栏的图标按钮; 代码实现: 1. 新建第一个自定义View, 图标 + 文字 的底部按钮; /** * 自定义控件

  • Android仿微信底部按钮滑动变色

    Android仿微信底部按钮滑动变色,这里只针对使用Fragment为Tab页的滑动操作,进行简单的变色讲解. 首先说下OnPageChangeListener这个监听 //这个监听有三个方法 public abstract void onPageScrollStateChanged (int state) public abstract void onPageScrolled (int position, float positionOffset, int positionOffsetPixe

随机推荐