Android仿微信列表滑动删除之可滑动控件(一)
这次是列表滑动删除的第三波,仿微信的列表滑动删除。先上个效果图:
前面的文章里面说过开源框架swipelistview的实现原理是每个列表item中包含上下两层view,普通状态下上层的view覆盖着下层的view,当用户滑开上层的view,下层的view就显示出来了。但是仔细观察微信列表的item,很明显并非这个实现方案,微信的item应该一个单层view,只不过这个item超出了所在的listview的宽度,在用户滑动item的时候,item超出屏幕的view则会显示在屏幕之上,这种滑动实现也很不错。
既然推测出微信的实现原理,现在就要寻找具体的实现方案了,我最开始想的比较简单,以为写一个横向线性布局linearlayout,让其包含两个子布局,左边的子布局宽度设置为填充父布局的宽度,以为另一个子布局就自然而然的会超出父布局的显示范围,但是具体测试的时候,发现就算左边的子布局设置为填充父布局的宽度,但实际显示的时候还是两个子布局被包含在父布局的显示范围内,右边的子布局无法做到超出父布局的显示范围。
纠结了一段时间,偶然情况下想起了scrollview还有一种类型是horizontalscrollview,,这个android提供的horizontalscrollview可以做到其子view超出它的显示范围,那我可以直接使用horizontalscrollview来实现这个item,我决定自定义一个控件,继承自horizontalscrollview,在代码里面直接添加两个子布局,并让左边的布局宽度填充这个控件的自身宽度,以便让右边的布局超出控件的显示范围,不过很不辛,horizontalscrollview比较傲娇,很难驾驭,奇葩bug层出不穷,譬如左边的子布局渲染出来了,但是右边的子布局愣是没初始化,更别说滑动了,弄的焦头烂额,最后实在没办法,只得暂时放下。
最后则是想到了使用一个自定义的viewgroup来实现这个item。现在很多app在第一次启动的时候,会出现一个介绍性的导航界面,用户一页一页的滑动,看完导航的介绍之后再正式进入app,现在这种导航介绍应该大多数是用viewpager实现的,viewpager可以做到两个滑动的子页同时显示在屏幕范围内,具有很好的体验效果。但是viewpager是在之后的support.v4里面引入的,最初并没有,那一开始这种导航介绍使用什么方案实现的呢?当然就是自定义的viewgroup了,其实supprot.v4.viewpager本身就是一个自定义的viewgroup。关于使用自定义的viewgroup实现导航介绍,csdn上有个大牛有专门写过一个文章介绍了,这里就不详细说了。
本篇的要讲的是如何使用自定义的viewgroup实现item的子view可以超出父布局的显示范围这样的效果。
写一个swipeitemview,继承自viewgroup,构造方法里面,传入左边布局的引用id和右边布局的引用id,初始化左子布局和右子布局,并将它们添加到swipeitemview中作为子view,代码如下:
private void init(context context, attributeset attrs) { mscroller = new scroller(context); ...... if(mprimaryviewid == -1) throw new runtimeexception( "illegal attribute 'primaryview', make sure you have set it"); mprimaryview = layoutinflater.from(getcontext()).inflate( mprimaryviewid, null); mprimaryview.setclickable(false); addview(mprimaryview, 0); if(mslidingviewid != -1) { mslidingview = layoutinflater.from(getcontext()).inflate( mslidingviewid, null); mslidingview.setclickable(false); addview(mslidingview, 1); } }
接下来需要重写viewgroup的onmeasure()方法,用来测量这个swipeitemview及其子view的宽高,其中先获取转入的heightmeasurespec中包含的heightsize,当heightsize的值和heightmeasurespec相同的时候,是测量整个swipeitemview的宽高,这是我们不做额外的处理,当heightsize不等于的传入的heightmeasurespec的时候,是用于测量swipeitemview它包含的子view的宽高,这里我们做一下额外的处理,主要是针对超出swipeitemview显示范围的右边的mslidingview,我想要它的宽只是包裹其内容就行,不想它的宽和屏幕范围等宽,所以构造一个这样的参数measurespec.makemeasurespec(0, measurespec.unspecified),具体代码如下:
@override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); int heightsize = measurespec.getsize(heightmeasurespec); int widthsize = measurespec.getsize(widthmeasurespec); int heightmode = measurespec.getmode(heightmeasurespec); int widthmode = measurespec.getmode(widthmeasurespec); if(heightsize != heightmeasurespec) { mprimaryview.measure(measurespec.makemeasurespec(widthsize, widthmode), measurespec.makemeasurespec(heightsize, heightmode)); if(mslidingview != null) { mslidingview.measure(measurespec.makemeasurespec(0, measurespec.unspecified), measurespec.makemeasurespec(heightsize, heightmode)); } } else { mprimaryview.measure(widthmeasurespec, heightmeasurespec); if(mslidingview != null) mslidingview.measure(widthmeasurespec, heightmeasurespec); } }
然后是重写viewgroup的onlayout()方法,用来放置子view在swipeitemview中的具体位置,主要就是让右边的mslidingview排列在左边的mprimaryview的右边,具体代码如下:
@override protected void onlayout(boolean changed, int l, int t, int r, int b) { mprimaryview.layout(l, t, r, b); if(mslidingview != null) mslidingview.layout(r, t, r + mslidingview.getmeasuredwidth(), b); }
好了,具体的swipeitemview的初始化完成了,接下来需要做什么工作呢,看一下上面的构造方法,有没有看到mscroller = new scroller(context)这行代码,我们需要使用这个mscroller来做这个swipeitemview滑动的动画效果。我们知道viewgroup中提供了scrollby()和scrollto()两个方法用来移动这个viewgroup视图内容的位置,其中scrollby()移动参数指定的距离,scrollto()方法移动到参数指定的位置。但是scrollby()还好,如果每次都是移动一小段距离的话,给用户的感觉就是一段连续的动画效果了,但是scrollto()则是瞬间移动,中间没有任何动画效果,会让人感觉到非常突兀,这样我们就需要用到mscroller这个对象了。
scroller是android提供的用来实现我们需要的移动动画效果的。scroller本身并非去执行移动的动画的,感觉上scroller更多是作为一个指挥者,当我们调用它的scroller.startscroll()方法的时候,这个方法我们需要传入移动初始的位置、移动的距离以及花费的时间,简单理解的话,我们假设指定的时间是10s,那第3s的时候,viewgroup视图内容应该移动到什么位置,第7s,应该移动到哪个位置,而且这个时刻scroller计算出来的位置可以通过scroller.getcurrx()和scroller.getcurry()获取到,这个过程,scroller会回调viewgroup中的computescroll()方法,在这个回调方法中,我们调用scrollto()执行具体的移动操作。而我们实现的这个scrolltowithanimation()方法就是提供给后面的swipelistview用来移动某个item并且使其移动过程带有动画效果的方法。代码如下:
@override public void computescroll() { if(mscroller.computescrolloffset()) { scrollto(mscroller.getcurrx(), mscroller.getcurry()); } } /** * just like scrollto(), but with animation :d * */ public void scrolltowithanimation(int scrollx, int scrolly) { mscroller.abortanimation(); mscroller.startscroll(getscrollx(), getscrolly(), scrollx - getscrollx(), getscrolly() - scrolly, 300); }
接下来需要实现一个自定义的listview,暂且命名为swipelistview。下一篇继续。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Java编程中的equals方法使用全解
下一篇: java 中锁的性能提高办法
推荐阅读
-
Android仿微信列表滑动删除之可滑动控件(一)
-
Android仿微信对话列表滑动删除效果
-
Android仿微信列表滑动删除之可滑动控件(一)
-
Android仿微信列表滑动删除 如何实现滑动列表SwipeListView
-
Android高仿微信对话列表滑动删除效果
-
Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能
-
Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能
-
Android实现顶部导航栏可点击可滑动效果(仿微信仿豆瓣网)
-
Android实现顶部导航栏可点击可滑动效果(仿微信仿豆瓣网)
-
微信小程序实战之仿android fragment可滑动底部导航栏(4)