仿微信朋友圈发表图片拖拽和删除功能

640?wx_fmt=png

今日科技快讯

中国联通在香港公布了上市公司2017年中期业绩。2017年上半年,公司主要业绩指标持续向好,收入稳步回升,服务收入达到人民币1,241.1亿元,同比增长3.2%。盈利能力按计划明显改善,EBITDA达到人民币435.6亿元,同比增长5.5%,EBITDA占服务收入比达到35.1%,同比提升0.8个百分点;权益持有者应占盈利(净利润)同比增长68.9%,达到人民币24.2亿元。2017上半年资本开支同比大幅下降49.5%,为人民币91.4亿元。得益于服务收入逐步改善以及资本开支大幅下降,公司自由现金流达到人民币375.2亿元。

作者简介

本篇来自 senyoung 的投稿,分享了一个仿微信朋友圈发表图片拖拽和删除功能,希望能对大家有所帮助。

senyoung 的博客地址:

http://my.csdn.net/u01323104

朋友圈实现原理

我们使用 Android Device Monitor 来分析朋友圈发布图片的界面实现原理。如果需要分析其他应用的界面实现也是采用这种方法哦。

打开 Android Device Monitor,选择DDMS,连接上真机,区域2 就会显示出当前手机正在运行的应用,再点击 区域1,然后新窗口就会显示出当前页面分析的结果,点击 区域3 中的相应控件,区域4 中就会选中对应的控件。区域4 中以菜单的层级关系显示出各控件的关系。

640?wx_fmt=jpeg

区域4中各参数意义:

eg:(0)FrameLayout[0,0][720,1280]

对应意义:(序号)控件名[x位置,y位置][宽度,高度]

我们这里主要分析与图片拖拽相关的布局实现!通过 4 中的控件层级关系,我们可以画出这样一个大概布局图:

640?wx_fmt=jpeg

从图中可以看出,微信团队使用了自定义的 GridView 实现了图片的拖拽和删除功能。

准备

以上分析得出微信使用的是自定义 GridView 实现 item 拖拽和删除功能。xxx,现在都什么年代了,还使用 GridView 吗?当然是使用流行的 RecyclerView 实现啦。如果只是单纯地使用 RecyclerView 就能实现的话,那世界就和平。这里还需要使用 ItemTouchHelper 对拖拽进行处理。

ItemTouchHelper

官方解释:

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView

一个让 RecyclerView 支持滑动删除和拖拽的实用工具类

  • 主要方法

640?wx_fmt=png

  • 使用

自定义一个类继承并实现 ItemTouchHelper.Callback 接口,以下方法必须实现:

640?wx_fmt=png

实现化 ItemTouchHelper 并关联 RecyclerView

640?wx_fmt=png

ItemTouchHelper.Callback

ItemTouchHelper 在拖拽和滑动删除的过程中会回调 ItemTouchHelper.Callback 的相关方法:

  • 主要方法

640?wx_fmt=png

实现过程

先放上最终效果图:

640?wx_fmt=gif

  • 布局文件

640?wx_fmt=jpeg

  • 自定义 MyCallBack 类继承并实现 ItemTouchHelper.Callbackr 的以下方法:

设置 item 只能处理拖拽事件,并能够向左、右、上、下拖拽

640?wx_fmt=jpeg

刷新 item

640?wx_fmt=jpeg

  • 关联 RecyclerView

640?wx_fmt=jpeg

  • 调整布局 

现在运行程序,界面如下:

640?wx_fmt=jpeg

手动黑人问号??? 仔细研究布局,原来 recyclerView 的高度设置成了wrap_content,因此无论怎么拖动,超出recyclerView的边界的部分就是不会显示滴!那就把高度设置成match_parent咯,重新调整后的布局如下:

640?wx_fmt=jpeg

再运行一次,我xx,还是不行。这是什么鬼,我不是已经设置了match_parent了吗?这一定有黑科技!经过一番思索,想起了一个不是经常使用的属性:clipToPadding;设置android:clipToPadding=”false”,果然可以。 

640?wx_fmt=jpeg

居然还有这种操作!

android:clipToPadding

Defines whether the ViewGroup will clip its children and resize (but not clip) any EdgeEffect to its padding, if padding is not zero. This property is set to true by default 
设置ViewGroup是否剪切其子View,通俗点讲就是:是否允许ViewGroup在padding中绘制子View。默认情况下,此属性设置为true,即不允许;

另外一个和 clipToPadding 比较相似的属性是:clipChildren;

android:clipChildren

Defines whether a child is limited to draw inside of its bounds or not. This is useful with animations that scale the size of the children to more than 100% for instance. In such a case, this property should be set to false to allow the children to draw outside of their bounds. The default value of this property is true 
一句话概括就是:是否允许子View超出父View

使用场景有:比如说外卖里的购物车效果

640?wx_fmt=jpeg

很明显画红色的地方是超出黑色的 View,可以将黑色 View 设置android:clipChildren=”false”,这样子 View 红色部分就可以超出父 View 大小。

还发现了一个比较诡异的问题,地址如下:

http://blog.csdn.net/u013231041/article/details/73603891

设置最后一个 item 不能移动

由以上分析可知,ItemTouchHelper.Callback 的 isLongPressDragEnabled() 可以设置是否支持长按拖拽,默认是 true ,即支持长按拖拽。现在我们要自定义指定哪些 item 可以拖拽,哪些不可以,因此我们需要重写 isLongPressDragEnabled():

640?wx_fmt=jpeg

取消了支持长按拖拽,就要自己处理 RecyclerView 的触摸事件,具体实现请参考源代码。 

在长按事件触发的时候调用以下代码:

640?wx_fmt=jpeg

最后还需要在 ItemTouchHelper.Callback 的 onMove() 中添加以下代码

640?wx_fmt=jpeg

还有要在 clearView() 方法里去 notifyDataSetChanged,不然 item 的 position 是没有交换的

640?wx_fmt=jpeg

设实现拖拽到底部放手删除功能

item 从拖动到放手的主要处理流程图如下:

640?wx_fmt=jpeg

那么我们应该在哪个地方去判断item是否到达删除区域呢?在前面介绍的方法,有这么个方法:onChildDraw,这个方法就会在item拖拽的过程不断回调并且返回item的偏移量。有了偏移量之后我们就很容易去判断item是否到达删除区域了。 

偏移量满足以下条件时,就到达删除区域: 
item偏移量>=RecyclerView的高-item底部到RecyclerView顶边的距离-TextView的高

640?wx_fmt=jpeg

问题来了,我们怎样判断用户在拖动后放手呢? 
我们用 boolean up 来标记,当 up 为 true 时手指抬起,false 为初始状态。在getAnimationDuration() 中设置其为true。记得需要在 clearView() 中恢复初始值 false ; 
还需要在 ItemTouchHelper.Callback 中暴露个接口 DragListener 给外部,用来提示通知外部什么时候显示删除区域,以及 item 进入删除区域时的文字提示。 
相关代码由于过长,这里省略,有兴趣的可以通过原文链接去学习。

图片相关

一看到图片或者说看到 Bitmap,会联想到什么?没错就是OOM!!!对于Bitmap导致的OOM要解决它是很有套路可言的。我们都知道图片压缩分为两种:质量压缩和尺寸压缩。可能有些人对这两个概念不是很清楚!他们的区别是质量压缩并不会改变图片的尺寸,而尺寸压缩则会改变图片的尺寸。当把以文件形式存在在硬盘上的图片,以Bitmap的形式加载到内存中的时候,我就必须进行尺寸压缩,尺寸压缩可以减少内存占用,这样就解决了OOM。详细原理参考文章,地址如下:

http://blog.csdn.net/tyk0910/article/details/53462728

对于质量压缩?主要用在图片传输上,用于提高传输速率,就像我们朋友圈发布图片时,它会对图片质量进行压缩,然后再传到后台,所以你在朋友圈看到的图片都不是原图片,都是经过压缩过的。这里推荐一个质量压缩效果和微信差不多的开源库,地址如下:

https://github.com/Curzibn/Luban

文末demo已实现图片尺寸压缩,即解决了OOM,也解决了拖拽不流畅问题。

总结

有时候我们看到一个觉得酷炫的功能,很想去实现他。这时候不必匆匆下手。我们自己可以先想一下要是我自己会怎样去实现,然后再用下 Android Device Monitor 大概分析下,更深一步可反编译一下看下源码,看他是怎样实现的,分析两者的优劣。对于这种情形:一个 View1 根据其他 View2 操作的手势或状态的改变而改变的,可以根据这样的套路去想:这个 View2 是否有提供这种状态反馈的方法什么的,我是否能把这个反馈传到 View1等。在不确定这个 View2 是否有你想要的方法时,请查看官方文档,请查看官方文档,请查看官方文档,重要的事件说三遍!比如我的删除区域是根据item的拖拽状态和距离去改变的,那么你就要去找 ItemTouchHelper.Callback 给我提供了什么回调啊,哪些方法可以返回这些东东啊,最后我就写个接口给通知外部。

demo地址:

https://github.com/kuyue/WeChatPublishImagesDrag

感谢阅读!欢迎拍砖。

更多

每天学习累了,看些搞笑的段子放松一下吧。关注最具娱乐精神的公众号,每天都有好心情。

640?wx_fmt=gif

如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。

欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号:

640?wx_fmt=jpeg

            </div>

640?wx_fmt=png

今日科技快讯

中国联通在香港公布了上市公司2017年中期业绩。2017年上半年,公司主要业绩指标持续向好,收入稳步回升,服务收入达到人民币1,241.1亿元,同比增长3.2%。盈利能力按计划明显改善,EBITDA达到人民币435.6亿元,同比增长5.5%,EBITDA占服务收入比达到35.1%,同比提升0.8个百分点;权益持有者应占盈利(净利润)同比增长68.9%,达到人民币24.2亿元。2017上半年资本开支同比大幅下降49.5%,为人民币91.4亿元。得益于服务收入逐步改善以及资本开支大幅下降,公司自由现金流达到人民币375.2亿元。

作者简介

本篇来自 senyoung 的投稿,分享了一个仿微信朋友圈发表图片拖拽和删除功能,希望能对大家有所帮助。

senyoung 的博客地址:

http://my.csdn.net/u01323104

朋友圈实现原理

我们使用 Android Device Monitor 来分析朋友圈发布图片的界面实现原理。如果需要分析其他应用的界面实现也是采用这种方法哦。

打开 Android Device Monitor,选择DDMS,连接上真机,区域2 就会显示出当前手机正在运行的应用,再点击 区域1,然后新窗口就会显示出当前页面分析的结果,点击 区域3 中的相应控件,区域4 中就会选中对应的控件。区域4 中以菜单的层级关系显示出各控件的关系。

640?wx_fmt=jpeg

区域4中各参数意义:

eg:(0)FrameLayout[0,0][720,1280]

对应意义:(序号)控件名[x位置,y位置][宽度,高度]

我们这里主要分析与图片拖拽相关的布局实现!通过 4 中的控件层级关系,我们可以画出这样一个大概布局图:

640?wx_fmt=jpeg

从图中可以看出,微信团队使用了自定义的 GridView 实现了图片的拖拽和删除功能。

准备

以上分析得出微信使用的是自定义 GridView 实现 item 拖拽和删除功能。xxx,现在都什么年代了,还使用 GridView 吗?当然是使用流行的 RecyclerView 实现啦。如果只是单纯地使用 RecyclerView 就能实现的话,那世界就和平。这里还需要使用 ItemTouchHelper 对拖拽进行处理。

ItemTouchHelper

官方解释:

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView

一个让 RecyclerView 支持滑动删除和拖拽的实用工具类

  • 主要方法

640?wx_fmt=png

  • 使用

自定义一个类继承并实现 ItemTouchHelper.Callback 接口,以下方法必须实现:

640?wx_fmt=png

实现化 ItemTouchHelper 并关联 RecyclerView

640?wx_fmt=png

ItemTouchHelper.Callback

ItemTouchHelper 在拖拽和滑动删除的过程中会回调 ItemTouchHelper.Callback 的相关方法:

  • 主要方法

640?wx_fmt=png

实现过程

先放上最终效果图:

640?wx_fmt=gif

  • 布局文件

640?wx_fmt=jpeg

  • 自定义 MyCallBack 类继承并实现 ItemTouchHelper.Callbackr 的以下方法:

设置 item 只能处理拖拽事件,并能够向左、右、上、下拖拽

640?wx_fmt=jpeg

刷新 item

640?wx_fmt=jpeg

  • 关联 RecyclerView

640?wx_fmt=jpeg

  • 调整布局 

现在运行程序,界面如下:

640?wx_fmt=jpeg

手动黑人问号??? 仔细研究布局,原来 recyclerView 的高度设置成了wrap_content,因此无论怎么拖动,超出recyclerView的边界的部分就是不会显示滴!那就把高度设置成match_parent咯,重新调整后的布局如下:

640?wx_fmt=jpeg

再运行一次,我xx,还是不行。这是什么鬼,我不是已经设置了match_parent了吗?这一定有黑科技!经过一番思索,想起了一个不是经常使用的属性:clipToPadding;设置android:clipToPadding=”false”,果然可以。 

640?wx_fmt=jpeg

居然还有这种操作!

android:clipToPadding

Defines whether the ViewGroup will clip its children and resize (but not clip) any EdgeEffect to its padding, if padding is not zero. This property is set to true by default 
设置ViewGroup是否剪切其子View,通俗点讲就是:是否允许ViewGroup在padding中绘制子View。默认情况下,此属性设置为true,即不允许;

另外一个和 clipToPadding 比较相似的属性是:clipChildren;

android:clipChildren

Defines whether a child is limited to draw inside of its bounds or not. This is useful with animations that scale the size of the children to more than 100% for instance. In such a case, this property should be set to false to allow the children to draw outside of their bounds. The default value of this property is true 
一句话概括就是:是否允许子View超出父View

使用场景有:比如说外卖里的购物车效果

640?wx_fmt=jpeg

很明显画红色的地方是超出黑色的 View,可以将黑色 View 设置android:clipChildren=”false”,这样子 View 红色部分就可以超出父 View 大小。

还发现了一个比较诡异的问题,地址如下:

http://blog.csdn.net/u013231041/article/details/73603891

设置最后一个 item 不能移动

由以上分析可知,ItemTouchHelper.Callback 的 isLongPressDragEnabled() 可以设置是否支持长按拖拽,默认是 true ,即支持长按拖拽。现在我们要自定义指定哪些 item 可以拖拽,哪些不可以,因此我们需要重写 isLongPressDragEnabled():

640?wx_fmt=jpeg

取消了支持长按拖拽,就要自己处理 RecyclerView 的触摸事件,具体实现请参考源代码。 

在长按事件触发的时候调用以下代码:

640?wx_fmt=jpeg

最后还需要在 ItemTouchHelper.Callback 的 onMove() 中添加以下代码

640?wx_fmt=jpeg

还有要在 clearView() 方法里去 notifyDataSetChanged,不然 item 的 position 是没有交换的

640?wx_fmt=jpeg

设实现拖拽到底部放手删除功能

item 从拖动到放手的主要处理流程图如下:

640?wx_fmt=jpeg

那么我们应该在哪个地方去判断item是否到达删除区域呢?在前面介绍的方法,有这么个方法:onChildDraw,这个方法就会在item拖拽的过程不断回调并且返回item的偏移量。有了偏移量之后我们就很容易去判断item是否到达删除区域了。 

偏移量满足以下条件时,就到达删除区域: 
item偏移量>=RecyclerView的高-item底部到RecyclerView顶边的距离-TextView的高

640?wx_fmt=jpeg

问题来了,我们怎样判断用户在拖动后放手呢? 
我们用 boolean up 来标记,当 up 为 true 时手指抬起,false 为初始状态。在getAnimationDuration() 中设置其为true。记得需要在 clearView() 中恢复初始值 false ; 
还需要在 ItemTouchHelper.Callback 中暴露个接口 DragListener 给外部,用来提示通知外部什么时候显示删除区域,以及 item 进入删除区域时的文字提示。 
相关代码由于过长,这里省略,有兴趣的可以通过原文链接去学习。

图片相关

一看到图片或者说看到 Bitmap,会联想到什么?没错就是OOM!!!对于Bitmap导致的OOM要解决它是很有套路可言的。我们都知道图片压缩分为两种:质量压缩和尺寸压缩。可能有些人对这两个概念不是很清楚!他们的区别是质量压缩并不会改变图片的尺寸,而尺寸压缩则会改变图片的尺寸。当把以文件形式存在在硬盘上的图片,以Bitmap的形式加载到内存中的时候,我就必须进行尺寸压缩,尺寸压缩可以减少内存占用,这样就解决了OOM。详细原理参考文章,地址如下:

http://blog.csdn.net/tyk0910/article/details/53462728

对于质量压缩?主要用在图片传输上,用于提高传输速率,就像我们朋友圈发布图片时,它会对图片质量进行压缩,然后再传到后台,所以你在朋友圈看到的图片都不是原图片,都是经过压缩过的。这里推荐一个质量压缩效果和微信差不多的开源库,地址如下:

https://github.com/Curzibn/Luban

文末demo已实现图片尺寸压缩,即解决了OOM,也解决了拖拽不流畅问题。

总结

有时候我们看到一个觉得酷炫的功能,很想去实现他。这时候不必匆匆下手。我们自己可以先想一下要是我自己会怎样去实现,然后再用下 Android Device Monitor 大概分析下,更深一步可反编译一下看下源码,看他是怎样实现的,分析两者的优劣。对于这种情形:一个 View1 根据其他 View2 操作的手势或状态的改变而改变的,可以根据这样的套路去想:这个 View2 是否有提供这种状态反馈的方法什么的,我是否能把这个反馈传到 View1等。在不确定这个 View2 是否有你想要的方法时,请查看官方文档,请查看官方文档,请查看官方文档,重要的事件说三遍!比如我的删除区域是根据item的拖拽状态和距离去改变的,那么你就要去找 ItemTouchHelper.Callback 给我提供了什么回调啊,哪些方法可以返回这些东东啊,最后我就写个接口给通知外部。

demo地址:

https://github.com/kuyue/WeChatPublishImagesDrag

感谢阅读!欢迎拍砖。

更多

每天学习累了,看些搞笑的段子放松一下吧。关注最具娱乐精神的公众号,每天都有好心情。

640?wx_fmt=gif

如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。

欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号:

640?wx_fmt=jpeg

            </div>

猜你喜欢

转载自blog.csdn.net/yzj_0722/article/details/80471838
今日推荐