本人是一只目前正在实习的android菜鸡,前段时间带我的大佬让我实现一个类似微信朋友圈和b站图片查看器酷炫UI效果,具体点就是点击图片列表中的某张缩略图后进入大图预览,这个过程中伴随着放大的动画,然后每一张大图都是viewpager中的一个item,并且大图都能被下拉缩小,达到条件后松开缩小到原来缩略图的位置,当然也伴随着缩小归位的动画,并且背景的透明度还会随着拖拽而不断变化。
说了这么多,作为一个菜鸡当时我是一脸懵逼的,于是上网求助,最后在github中看到了一个大神的开源框架ImageViewer,运行demo后发现实现的效果很赞,完美符合我的需求。粗略的看了下代码,觉得以我目前的水平勉强能够理解,于是我斗胆决定开始分析和学习它。由于本人水平有限,要是有什么说错的地方还望见谅= ̄ω ̄=
首先是项目的地址:https://github.com/albert-lii/ImageViewer。我把它下载到了本地,并用android studio将它打开。
作者的demo中预设了五种场景,我选择了看上去最简单的那一个当作入口开始学习,效果是这样的:
最简单的就是这个对单图的操作了,接下来就开始要去作者的代码海洋里游一游,看看作者是如何实现这个酷炫的UI效果的吧!在这里我是有选择地贴代码块,所以阅读本文需要把项目下载后打开,配合着看。
首先是这个ImagePagerAty的布局文件aty_image_pager:
很简单,就是一个ImageView用于加载缩略图,另一个则是自定义的View:ScaleImageView,作者对它的描述是:可缩放图片的自定义 View(即 viewPager 的 item),显然这个就是最核心的部分,也就是之后要重点(吐血)分析的一个类。
然后是ImagePagerAty:
public class ImagePagerAty extends BaseActivity {
private ImageView imageView;
private ScaleImageView imagePreview;
private ViewData mViewData;
private Point mScreenSize;
private boolean isCancelByBack;
private WxImageDragger mDefDragger;
......
}
首先是声明的五个变量及说明
imageView:用于展示缩略图
imagePreview:自定义的ScaleImageView,它继承自FrameLayout
mViewData:这里面封装了视图的相关信息,包括目标view的横纵坐标,宽高,以及图片的原始宽高。
mScreenSize:在这里通过getScreenSize()记录手机屏幕的宽高
isCancelByBack:标记位,用于监听返回键(这里我有个疑问,之后再说)
mDefDragger:图片拖拽时的辅助类,例如计算 view 的坐标,缩放比例,背景透明度等
下面看看ImagePagerAty里的一些方法:
private void initView() {
imageView = findViewById(R.id.imageView);
imagePreview = findViewById(R.id.imagePreivew);
mViewData = new ViewData();
mScreenSize = ImageViewerUtil.getScreenSize(this);
mDefDragger = new WxImageDragger();
mDefDragger.setBackground(imagePreview.getBackground());
imagePreview.setDefSize(mScreenSize.x, mScreenSize.y);
imagePreview.setImageDraggerType(ImageDraggerType.DRAG_TYPE_WX);
}
在initView()中主要是做一些初始化的工作,从中可以看出,imagePreview的默认尺寸就是屏幕的宽高,这里还未imagePreview设置的拖拽类型,原因是作者一共提供了两种拖拽的效果,由于ImagePagerAty中展示的是模仿微信朋友圈的图片查看效果,故设置为DRAG_TYPE_WX。
private void addListener() {
imagePreview.setOnViewClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
imagePreview.cancel();
}
});
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int[] location = new int[2];
// 获取在整个屏幕内的绝对坐标
imageView.getLocationOnScreen(location);
mViewData.setTargetX(location[0]);
// 此处注意,获取 Y 轴坐标时,需要根据实际情况来处理《状态栏》的高度,判断是否需要计算进去
mViewData.setTargetY(location[1]);
mViewData.setTargetWidth(imageView.getWidth());
mViewData.setTargetHeight(imageView.getHeight());
imagePreview.setViewData(mViewData);
imagePreview.start();
}
});
}
在addListener()中,首先为大图,也就是imagePreview设置点击事件, imagePreview.cancel()执行的同时,就伴随着缩小归位的动画。之后是为缩略图imageView设置点击事件,这里面要做的就是获取小图的坐标值和宽高,并将它们设置给大图imagePreview,自然,imagePreview.start()执行时就是伴随着图片从缩略图放大到全屏的动画。
private void loadImage() {
GlideUtil.loadImage(this, Utils.getImageList().get(1), new SimpleTarget() {
......
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition super Drawable> transition) {
if (resource != null) {
imagePreview.hideProgress();
imageView.setImageDrawable(resource);
imagePreview.getImageView().setImageDrawable(resource);
mViewData.setImageWidth(resource.getIntrinsicWidth());
mViewData.setImageHeight(resource.getIntrinsicHeight());
}
}
});
}
这是ImagePagerAty中最后一个比较重要的方法,GlideUtil里封装了Glide加载网络图片的一些操作,重要的就是上面代码所呈现的,在onResourceReady中先是把网络图片加载到缩略图中,然后拿到该图片的原始宽高(比如说720*1280),并将这个信息设置到mViewData中。
到这里,准备工作已经做完,接下来我们就一起来看看这其中涉及到的一些类里面都做了那些事儿吧!