照片已选列表与大图浏览的机制、实现方案和相关文档

照片已选列表与大图浏览的机制、实现方案和相关文档
1. 照片已选列表

1.1 已选列表功能需求:
1.1.1 动态增删,列表视图需要动态添加元素改变长度。
1.1.2 单击(Click Action)item查看大图。
1.1.3 横向滚动(Scroll Action),列表超出屏幕宽度,可以横向滚动查看。
1.1.4 长按item(Long Click Action)可以拖动交换item,实现照片顺序的改变。
1.2 控件结构,如图:

最外层是一个自定义的public class GalleryHorizontalView extends HorizontalScrollView,这里当时是为了重写onInterceptTouchEvent,让其返回false,将事件往下层view传递。其实多余,完全可以直接用HorizontalScrollView,在内层view里调用getParent().requestDisallowInterceptTouchEvent(true)即可实现。这个HorizontalScrollView主要负责横向显示列表,由于onInterceptTouchEvent里返回了false,所以其他的作用全都屏蔽,后面会提到屏蔽的原因。
内层一个Linearlayout,orientation为horizontal是为了让内部空间可以横着排列。
最内层是个public class DragGridView extends GridView,这里委屈让gridview变成了一个listview,只显示一行,不过以后可能会发挥它的作用——已选列表变成全屏查看grid的形式。
1.3 Android事件传递机制:
参考资料:Android onTouch事件传递机制
HorizontalScrollView本来会拦截并处理Scroll Action,但这会跟长按、长按拖动的事件冲突,所以我们这里其实是直接屏蔽了外层HorizontalScrollView的事件处理,所有事件都会直接到达DragGridView,在DragGridView里进行。
1.4 已选列表功能需求的实现(代码见DragGridView.java)
1.4.1 动态增删:
当列表有变化时,就使用LayoutParams重新设置DragGridView的宽度,并更新NumColumns等。
1.4.2 单击(Click Action)、横向滚动(Scroll Action)、长按item(Long Click Action)行为:
1.4.2.1 单击事件流程:
1.4.2.1.1 在dispatchTouchEvent里,ACTION_DOWN,执行:
mHandler.postDelayed(mLongClickRunnable, dragResponseMS);//该方法表示dragResponseMS毫秒后,//mLongClickRunnable线程会执行,也就是进入了长按时间。
1.4.2.1.2 在不足dragResponseMS时间之前就进行了其他动作比如move出了控件外(微小的move是可以接受的)或者up了,则:mHandler.removeCallbacks(mLongClickRunnable);,长按事件取消。
1.4.2.1.3 然后进入onTouch里,onTouch方法里对不同的行为做不同的操作,所以在onTouch里会进行判断识别到底当前是什么行为。这里需要注意,判断是否是单击事件不仅仅是在dragResponseMS时间内有个action_down+action_up,因为人手操作的不灵敏,可能会有一个小的move操作,所以,需要计算CalDis.calDis(mDownX, mDownY, mUpX, mUpY)<3 也算作是一个单击事件。
1.4.2.2 横向滚动事件流程:
1.4.2.2.1 前两步和单击事件流程相同
1.4.2.2.2 到onTouch里,如果CalDis.calDis(mDownX, mDownY, mUpX, mUpY)>=3,并且此时没有执行mLongClickRunnable,则开始处理横向滚动事件。
1.4.2.2.3 横向滚动时间分为两部分:在ACTION_MOVE时,要跟随手指的MOVE来Scroll列表(对应线程galleryHorizontalScrollRunnable);在ACTION_UP的时候要以一定速度惯性滑动然后停止,这需要根据fling的时间距离来计算这个速度(对应线程galleryHorizontalScrollRunnable)。
1.4.2.3 长按事件:
mLongClickRunnable,此方法将原item隐藏,然后创造一个供拖动的幻象,程序进入长按拖动交换图标的行为系列里:move时调用onDragItem,up时调用onStopDrag。
此处的思路:
1. 根据手指按下的X,Y坐标来获取我们在GridView上面点击的item
2. 手指按下的时候使用Handler和Runnable来实现一个定时器,假如定时时间为1000毫秒,在1000毫秒内,如果手指抬起了移除定时器,没有抬起并且手指点击在GridView的item所在的区域,则表示我们长按了GridView的item
3. 如果我们长按了item则隐藏item,然后使用WindowManager来添加一个item的镜像在屏幕用来代替刚刚隐藏的item
4. 当我们手指在屏幕移动的时候,更新item镜像的位置,然后在根据我们移动的X,Y的坐标来获取移动到GridView的哪一个位置
5. 到GridView的item过多的时候,可能一屏幕显示不完,我们手指拖动item镜像到屏幕下方,要触发GridView想上滚动,同理,当我们手指拖动item镜像到屏幕上面,触发GridView向下滚动
6. GridView交换数据,刷新界面,移除item的镜像
2. 大图浏览
2.1 ViewPager
有良好的内存管理机制,预加载、释放内存都比较安全。
资料网上很多,具体用法不再赘述。
2.2 ZoomImageView
ViewPager的instantiateItem方法类似于View里的getView方法,这里加载的item是zoom_image_layout,该layout是个自定义视图com.jihox.pbandroid.view.ZoomImageView,这个视图支持两点触控的的缩放,在放大的情况下可以单点移动查看。
实现原理:首先在ZoomImageView里我们定义了四种状态,STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE,这四个状态分别代表初始化、放大、缩小和移动这几个动作,然后在构造函数里我们将当前状态置为初始化状态。接着我们可以调用setImageBitmap()方法把要显示的图片对象传进去,这个方法会invalidate一下当前的View,因此onDraw()方法就会得到执行。然后在onDraw()方法里判断出当前的状态是初始化状态,所以就会调用initBitmap()方法进行初始化操作。
那我们就来看一下initBitmap()方法,在这个方法中首先对图片的大小进行了判断,如果图片的宽和高都是小于屏幕的宽和高的,则直接将这张图片进行偏移,让它能够居中显示在屏幕上。如果图片的宽度大于屏幕的宽度,或者图片的高度大于屏幕的高度,则将图片进行等比例压缩,让图片的的宽或高正好等同于屏幕的宽或高,保证在初始化状态下图片一定能完整地显示出来。这里所有的偏移和缩放操作都是通过矩阵来完成的,我们把要缩放和偏移的值都存放在矩阵中,然后在绘制图片的时候传入这个矩阵对象就可以了。
图片初始化完成之后,就可以对图片进行缩放处理了。这里在onTouchEvent()方法来对点击事件进行判断,如果发现有两个手指同时按在屏幕上(使用event.getPointerCount()判断)就将当前状态置为缩放状态,并调用distanceBetweenFingers()来得到两指之间的距离,以计算出缩放比例。然后invalidate一下,就会在onDraw()方法中就会调用zoom()方法。之后就在这个方法里根据当前的缩放比例以及中心点的位置对图片进行缩放和偏移,具体的逻辑大家请仔细阅读代码,注释已经写得非常清楚。
然后当只有一个手指按在屏幕上时,就把当前状态置为移动状态,之后会对手指的移动距离进行计算,并处理了边界检查的工作,以防止图片偏移出屏幕。然后invalidate一下当前的view,又会进入到onDraw()方法中,这里判断出当前是移动状态,于是会调用move()方法。move()方法中的代码非常简单,就是根据手指移动的距离对图片进行偏移就可以了。
注意:当view放大后,图片超过了一屏幕,需要调用getParent().requestDisallowInterceptTouchEvent(true) 来阻断外层Viewpager的事件拦截,zoomImageView才能单指移动查看。但放大了仍然需要翻页,所以应该在单指移动到图片边缘的时候调用getParent().requestDisallowInterceptTouchEvent(false),让外层ViewPager拦截事件,实现翻页:

2.3 ViewPager的弹性效果:
在viewpager的头尾各加一个空白页,在onPageSelected时,若index==0,则仍然返回第一页,当index==size+1时,返回第size页。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值