Android中使用PhotoView与Picasso切换图片时保留缩放及坐标的问题
解决PhotoView切换不同分辨率图片时保留缩放比例与相对坐标的问题
嘿,我明白你现在的困扰——切换不同分辨率的图片时,虽然缩放比例保住了,但图片直接居中,没法停留在之前关注的位置对吧?这是因为两张图的尺寸不一样,绝对的XY坐标没法直接复用,得转换成相对图片的比例位置才行,我来给你调整代码解决这个问题:
核心思路
- 切换前,把当前的滚动坐标转换成相对于旧图的比例(比如当前可视区域左上角在缩放后图片中的位置占总可滚动范围的比例)
- 新图加载完成后,用同样的缩放比例,再把这个相对比例转换成新图对应的绝对滚动坐标,最后设置给PhotoView
修改后的完整代码
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { // ... 你的基础初始化代码 mRequestCreator1 = Picasso.get().load(mImageUri1); mRequestCreator2 = Picasso.get().load(mImageUri2); mRequestCreator1.into(mPhotoView); mBtnSwitch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 1. 记录当前PhotoView的状态 final float prevScale = mPhotoView.getScale(); final int prevScrollX = mPhotoView.getScrollX(); final int prevScrollY = mPhotoView.getScrollY(); Drawable oldDrawable = mPhotoView.getDrawable(); if (oldDrawable == null) { // 如果还没加载完,直接切换,不处理坐标 switchImage(); return; } // 2. 计算相对偏移比例(基于旧图的可滚动范围) final int oldImgWidth = oldDrawable.getIntrinsicWidth(); final int oldImgHeight = oldDrawable.getIntrinsicHeight(); // 缩放后图片的总宽度 - View宽度 = 横向可滚动的最大范围 final float scrollableWidthOld = oldImgWidth * prevScale - mPhotoView.getWidth(); final float scrollableHeightOld = oldImgHeight * prevScale - mPhotoView.getHeight(); final float relativeX; final float relativeY; if (scrollableWidthOld <= 0 || scrollableHeightOld <= 0) { // 缩放比例为1或图片比View小,没有滚动空间,默认居中比例0.5 relativeX = 0.5f; relativeY = 0.5f; } else { // 计算相对比例,范围0~1 relativeX = (float) prevScrollX / scrollableWidthOld; relativeY = (float) prevScrollY / scrollableHeightOld; } // 3. 加载新图并在回调中恢复状态 Callback callback = new Callback() { @Override public void onSuccess() { Drawable newDrawable = mPhotoView.getDrawable(); if (newDrawable == null) return; // 设置缩放比例(关闭动画避免跳变) mPhotoView.setScale(prevScale, false); // 计算新图的可滚动范围 int newImgWidth = newDrawable.getIntrinsicWidth(); int newImgHeight = newDrawable.getIntrinsicHeight(); float scrollableWidthNew = newImgWidth * prevScale - mPhotoView.getWidth(); float scrollableHeightNew = newImgHeight * prevScale - mPhotoView.getHeight(); // 转换成新图的绝对滚动坐标 int newScrollX = scrollableWidthNew <= 0 ? 0 : (int) (relativeX * scrollableWidthNew); int newScrollY = scrollableHeightNew <= 0 ? 0 : (int) (relativeY * scrollableHeightNew); // 滚动到目标位置 mPhotoView.scrollTo(newScrollX, newScrollY); } @Override public void onError(Exception e) { e.printStackTrace(); } }; if (sPrevIsOriginal) { mRequestCreator2.into(mPhotoView, callback); } else { mRequestCreator1.into(mPhotoView, callback); } sPrevIsOriginal = !sPrevIsOriginal; } // 提取切换图片的逻辑,方便无状态时调用 private void switchImage() { if (sPrevIsOriginal) { mRequestCreator2.into(mPhotoView); } else { mRequestCreator1.into(mPhotoView); } sPrevIsOriginal = !sPrevIsOriginal; } }); }
关键细节说明
- 相对比例计算:用当前滚动距离除以旧图缩放后的可滚动总范围,得到0~1之间的比例,这个比例和图片分辨率无关,能精准对应用户关注的相对区域
- 边界处理:当图片缩放后比View还小时,没有滚动空间,直接用0.5的比例保持居中
- 缩放动画控制:
setScale的第二个参数设为false,关闭缩放动画,避免切换时的跳变感 - 滚动时机:必须先设置缩放比例再调用
scrollTo,因为缩放会改变可滚动范围,顺序错了会导致位置偏移
内容的提问来源于stack exchange,提问作者JSong
相关产品推荐
相关产品推荐

