学徒浅析Android开发:杂谈——仿QQ列表左右滑动效果

本篇文章已授权微信公众号guolin_blog(郭霖)独家发布

 

ListView可以说是最常用的控件了,所谓在平凡中创建不平凡,各种listView的衍生版本层出不穷。在商业应用中,一个item上堆放的信息不能太多,item本身的作用在于提供给用户快速选择的权利,信息堆放太多,反而无法让用户快速决定,所以简洁明了才是真。但是对于一些在场景中必要的功能按键,就需要找到合适的放置位置,尤其是现在大家公司里的美工们都是缺乏激情的MM,多给她们提供一些布局选择,你脱单的希望就增大了一些。

QQ列表的效果一开始并没有吸引我,直到我发现我的测试手机(联想S650)中通讯录列表的item也是可以左右滑动。我的好奇心才被激起了。下面就来讲讲怎么实现这个功能:

 

布局样式及构建

    我们把布局分成三部分:左侧布局,中央布局,右侧布局。其中中央布局就是在应用中用来正常展示,左侧和右侧布局是隐藏布局,样式如下:

左侧布局,中央布局,右侧布局都是独立的布局,他们之间互不干扰

我们如何把他们组合起来呢,这就需要重写onMeasure()onLayout()方法了,,所以我们来自定义一个FrameLayout来放置这三个布局。因为我们需要的是一个滑动的效果,所以给这个自定义控件添加一个Scroller来管理滑动。这样基本属性和对象我们就搭建出来了

 

之所以设置isLeftExistisRightExist,是为了提高控件的利用率,缺少哪一个布局,就禁止相反方向的滑动。 也是对程序逻辑的一种保护。

这个布局控件继承了FrameLayout,在xml编辑器中布局时,我们都知道FramgLayout的特点,控件的顺序是从左向右依次放置的,所以我们在重写构造函数时,要调用addView把左中右三个布局安从左到右的顺序添加上。


Scroller作为滑动管理器,我们也需要在构造函数中声明,创建时我们可以选择传或者不传滑动插值器的类型,默认不传的话,就是匀减速插值器DecelerateInterpolator,这里我设置为反弹插值器BounceInterpolator


接下来,我们先重写onMeasure()方法,onMeasure()方法是用来计算布局的大小的,也是最先被调用的方法,只有知道的布局的大小,设备才能正常绘制出我们的布局。

在这里我们不需要特别声明三部分布局的高宽,只需要声明宽是自适应,高为设备适应后的固定尺寸就行。这样做可以保证布局的正常展示,不会出现压缩现象。在声明过程中,我们需要使用MeasureSpe.makeMeasureSpec来指定宽高(这个方法是onMeasure()原有形参调用的方法,在这里我们要保持数据的一致性,所以也要使用这个方法),它需要两个参数,一个是大小size,一个是模式mode,其中MeasureSpec.UNSPECIFIED表示当前模式为自适应,MeasureSpec.EXACTLY表示当前模式为确定值或者是充满(match_parent)。

 

 

  下一步重写onLayout()方法,放置布局。因为手机屏幕左上方的位置是(0,0)点,屏幕左侧为负值,右侧为正值,而我们的左中右三个布局,必须要依次放置,View的layout()方法中只使用的左上角和右下角的坐标,这样我们就可以得出左中右三布局的位置坐标:

 

Scroller滑动设置

     首先明白一点,滑动的实际原理,就是布局坐标发生变化,在不断的重绘过程中,产生有规律的位移。这样我们就需要两个要点:

1、本次重绘中,需要位移的距离。

2、重绘的时机。

很幸运,Scroller提供给我们了滑动时的位置变化,滑动的样式,滑动的时机,以及滑动的结束判断标记,所以我们只要伺候好Scroller就行了。之前我们已经在构造函数中初始化了一个Scroller对象,下面为了让我们的控件可以滚动起来,我们需要重写控件的computeScroll()方法,顾名思义,是计算滑动距离的,这个方法在重绘时会被触发,但这个方法在ViewGroup中本身是没有具体操作,所以我们看不到什么变化。现在我们需要在这里重新获得当前位移的距离,并调用postInvalidate()进行重绘。位移的距离由Scroller.getCurrX()提供。这个方法可以实时获得当前移动轨迹下X轴的坐标变化。移动轨迹由Scroller.startScroller()触发,它有五个参数:


滚动发起后,我们可以通过computeScrollOffset()来判断当前滚动是否结束,如果是,则为false,不是为true。有了坐标点的移动轨迹,我们就可以在滚动过程中不停地重新设置布局的坐标,重绘布局,从而实现视觉上的滚动效果。

 

因为我们手指滑动的操作是左右滑动,所以在计算移动距离时,直接让布局移动Scroller.getCurrX()个距离就行,而当关闭布局时,直接移动 - 中央布局.getLeft()(就是代码的baseX) - Scroller.getCurrX()个距离就行,为什么取负值,因为负值表示是向相反的方向滑动。 


ListView的监听

ListView方面,我们只需要重写onTouchEvent()就行了,对用户触摸事件进行区分,同时为了实现上下滑动时恢复原来位置,我们需要分别记录当前视图和历史视图,当前位置和历史位置。在对应的MotionEventAction()中作如下操作:

     1、MotionEvent.ACTION_DOWN

       (1)获取当前的视图对应的位置,以及用户的点击点坐标。

        (2)判断是否存在历史视图(即前期是否有未关闭的视图),若存在,先对历史视图进行归位操作。

        (3)将当前视图备份到历史视图中

2、MotionEvent.ACTION_MOVE

   (1)判断滑动手势,是上下滑动还是左右滑动,如果是上下滑动,直接中断


判断原理:比较X轴点间距和2PX倍的Y轴点间距的大小,前者大于后者,是左右滑动;前者小于后者,是上下滑动。

   (2)判断左右滑动的方向,计算滑动距离。

        先判断当前视图的状态标记,如果是关闭的:

        滑动距离 = 手指滑动距离;

        如果是准备展示左视图:

滑动距离 = 手指滑动距离 - 待展开视图的宽;

        如果是准备展示右视图:

滑动距离 = 手指滑动距离 + 待展开视图的宽;

(为什么有加减区别,参看上方stratScroll参数解释)

   (3)执行滑动操作。

3、MotionEvent.ACTION_UP

       作为点击状态的最后一步,这里我们需要根据当前视图的滑动状态,来做一下补偿操作。即判断当前隐藏的视图是否完全显示出来或隐藏掉。详细判断大家可在代码中看到。这里我考虑到的场景有如下几个:

向左滑动:
场景1、隐藏左侧菜单

  场景2、弹出右侧菜单

  场景3、隐藏左侧菜单后直接弹出右侧菜单

  向右滑动:

场景1、隐藏右侧菜单

  场景2、弹出左侧菜单

  场景3、隐藏右侧菜单后直接弹出左侧菜单

 

 

整个控件的开发思路就是这样了,对于滑动操作,没有什么复杂的,Android已经给你提供了Scroller来完成这一系列操作。我们可以按照如下的流程图,快速制作出项目中需要的控件:

 

 

下载地址:

代码已提交到CSDN上了,大家有好的修改意见,欢迎提出交流。留个QQ:1013608582,方便大家丢香蕉。

http://download.csdn.net/detail/lz8362/9560104

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值