Android仿朋友圈照片定点放大和滑动查看(未完待续)

    最近公司的项目中有一个图片加载和查看功能实现,具体是说通过列表项加载小图片,点击放大图片并且可以左右翻页,放缩等等,类似于微信朋友圈的照片墙功能。拿到这个需求后我想了想,图片列表首选当然是RecyclerView啦,线性布局,网格布局,瀑布流布局应有尽有,针对这个需求当然是网格布局咯。网络加载图片库那就多啦,从最初的Universal ImageLoader,到如今的Picasso,Fresco,Glide等等,都是很优秀的图片加载框架。作为一个喜欢回忆,有着一丝丝文艺风的程序猿(害羞),我还是选择了经典却又不失美感的Universal ImageLoader。好了,第一个页面(列表展示页)的构建思路有了,那么,点击小图放大后的页面用什么实现呢?dialog?还是Activity?果断Activity啦,毕竟图片查看页面要做的事情很多,Activity更能满足我们的需求,下面,怎么翻页查看?果断ViewPager,多么好用的工具。图片手势缩放用什么?photoView呗,GitHub那么高的star,咱们先用着再说。好了,工具选好了,下面就是怎么实现代码了,让我们一步一步来。

   步骤一:MainActivity

   MainActivity就是我们用来展示图片列表页的Activity了,布局文件只有一个RecyclerView,不再赘述。

   1.1 初始化universal ImageLoader

   这里把imageLoader的初始化放在了Application当中,没啥可说的,如图所示


  1.2初始化view和数据

     

   其中,定义网格布局的时候我们设置了子项之间的间距,继承了ItemDecoration类,子类实现如下所示:

 

注释应该已经很清楚了,除了每行第一个,其他子项的左边和下边都留出间距。

   1.3 RecyclerView adapter

   适配器里面没啥好说的,我们在方法中控制子项的宽高几乎相等


通过获取imageView的param设置宽高就行。

   1.4 子项点击事件的处理

   到这里,图片列表页的实现也没啥要特别说明的地方。展示效果如图所示



现在,关键点来了,不知道大家有没有注意看过,朋友圈里的照片,点击哪张照片就从哪张照片处放大,关闭大图后又会缩小到当前的小图。这里有两个关键点:

1.点击哪张小图,放大效果就从点击的小图处放大;

2.放大后关闭大图,就会从当前大图缩小至对应的小图。

比如,我点击第1张小图,放大效果从第1张小图处放大,此时我左滑翻页到第3张大图,关闭大图后缩小效果会缩小至第3张小图处。因此,很明显的情况是,当点击事件发生的时候我们不仅需要记录所点击的小图的中心点坐标,此外还需记录其他所有小图的中心坐标,因为我在大图中可以随便左右翻页,翻到哪张大图就要缩小到对应的小图。因此,我们的思路很清晰了,就是要记录所有小图的坐标。因此,我们就会这样写:

for(int i=0;i<rv.getChildCount();i++)

{

   View view=rv.getChildAt(i);//获取所有子项View

   int xy[]=new int[2];

   view.getLocationOnScreen(xy);//获取子项左上角在屏幕中的坐标

   float[] xyf=new float[]{xy[0]*1.0f+view1.getWidth()/2,xy[1]*1.0f+view1.getHeight()/2};//获取子项View的中心点坐标
   xyMap.put(i, xyf);//加入到坐标集合中

}

   乍看起来似乎没啥问题,运行也没问题,但是运行后发现图片数量少了,哪去了?因为这里的getChildCount只包含当前手机页面可见的子项个数,并不是所有子项的个数,比如我总共有25张小图片,当前页面可见小图是15张,那么getChildCount就是15,那么我们换成rv.getAdapter.getItemCount()就行了,确实此时返回的是所有子项的个数。但是此时又带来一个新的问题,不可见的子项哪有坐标哦?是的,不可见的子项在屏幕中确实没有坐标。大家再看朋友圈,找一条包含4张以上照片的朋友状态,接着上下滑动页面,使得当前手机屏幕隐藏掉部分小图,比如就像下面这样:


例如,我的某个朋友状态中有9张图,我现在使得第3行的图不可见,此时我点击第1张放大查看,接着在大图中左滑到最后一张,再点击大图缩小,你会发现最后一张大图缩小到了九宫格的右下角(即横坐标属于第3列,纵坐标属于手机屏幕下边界)。再来一遍,点击大图滑动到第8张图,点击缩小,缩小效果到了中间的下边界(即横坐标属于第2列,纵坐标属于手机屏幕下边界)。同理,隐藏第1行图片,从最后一张点击放大,滑动到第一张大图后缩小,会缩小至第1列,手机屏幕上边界。此时我们明白了,朋友圈中对于不可见的子项图片,保留了其所在列的信息,缩小至上边界或者下边界。因此,我们代码可以这样写:


   代码中注释很清楚,首先获取可见的第一个子项,获取它的position,position数值代表的就是在所有子项中的索引,如果position0>0,说明当前页面隐藏了前面的部分图片,因此for循环将0~position0的之间的不可见的子项根据列的信息赋值坐标,即列信息和上边界。同理,第2个for循环判断对于position=-1的(即不可见子项)赋值列信息和下边界,循环完毕,所有子项坐标信息获取完成。

   1.5 点击放大效果

   MainActivity完成了,坐标也有了,怎样点击放大呢?在XML中定义动画肯定不行,因为XML中的动画中心是写死的,我们这是活的。我们知道,Activity中包含了View树结构,根View包含子View等等,因此我们的思路很简单,使SecondActivity保持透明状态,根View(即Layout)设置成黑色背景,将ScaleAnimation放大动画作用于根View中,呈现的效果应该就是我们想要的。如下是设置Activity透明:


前提我们需要先禁用Activity自带的切换动画,即

overridePendingTransition(0,0);

因此,我们重写startActivity方法,如下所示:


为什么要这样写,因为Activity的API中说了啊,overridePendingTransition方法在startActivity或者finish方法后立即调用,如下:


至此,MainActivity完成。


步骤二:SecondActivity

   SecondActivity就是我们查看大图的Activity啦。

2.1 放大效果

根据MainActivity传来的所有子项的坐标以及当前点击的子项position,我们这样实现动画


先得到当前position的坐标,动画作用于最外层的LinearLayout,大小从0到1,动画加载完成后初始化数据,代码注释很清楚。另外,这个Activity设置成全屏显示哦,不再赘述。

2.2 加载数据


根据传来的URL集合,我们网络加载大图到photoView,图片设置为等比例缩放居中显示,大图中可以双击放缩或者手势放缩,单击图片退出大图模式。

2.3 大图左右滑动


ViewPager有个滑动监听,在里面更新当前的缩放中心点和当前是第几张图片。

2.4 重写finish方法

上一节说道,overridePendingTransition方法也会立即调用在finish方法后,因此我们也要禁用Activity默认的退出动画,再设置我们的缩小动画,如下:


其中,有个注意点,大图页面是全屏,小图页面非全屏,从全屏退到非全屏页面会产生抖动现象,因此退出前设置成非全屏模式。其他代码注释很清楚,无需赘述。

效果图如下所示:


至此,全部代码完成。其实,本代码只是一种实现思路,大家可能有更好的更优化的思路,比如View预渲染等等,本人在这里只是提供了一种简易的实现方法,侧重于细节的实现以及各方面的注意点,后续还有很多优化的空间。因此,希望大家踊跃评论,有不当之处及时指出,技术就是不断讨论才会不断进步,谢谢大家,未完待续~

完整源码下载地址,我的GitHub:https://github.com/HarrisonChe/ImageObserverDemo

发布了10 篇原创文章 · 获赞 89 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/cbzcbzcbzcbz/article/details/80134836