Android PhotoView基本功能实现

本文详细介绍了在Android-23 SDK中实现PhotoView的具体步骤和技术要点,包括自定义View展示图片、处理缩放和滑动手势等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android开发过程中,想必都使用过PhotoView来实现图片展示的功能。在最新版的sdk(android-23)有了一个原生的photoView,并且代码实现也很简单,逻辑也很清晰。我们在实际的工作中,遇到的需求可能与这些photoview现有功能有些细微的差别,需要修改,或者重新开发。本文简单介绍下android-23中photoview涉及到的相关技术,相信读者看完后会发现,其实很简单。以下为实现思路和步骤

  • 1 自定义一个View

通过自定义视图,继承View,为外界提过public接口方法来设置drawable,或者提供自定义属性在layout文件中设置drawable。在onDraw方法中,将drawable画到canvas上。

  • 2 首次显示drawable

图片首次显示时,一般要居中全部显示在屏幕内,并且至少x轴或y轴方向占满屏幕。如何实现?

对于一个drawable,可以获取其原始的宽、高:

int drawableWidth = mDrawable.getIntrinsicWidth();
int drawableHeight = mDrawable.getIntrinsicHeight();

然后将原始的宽、高,保存到一个RectF对象中:

private RectF mTmpRectSrc = new Rect(); 
mTmpRectSrc.set(0, 0, drawableWidth, drawableHeight);

自定义的视图在measure完之后,会有一个宽、高:

int viewWidth = getWidth();
int viewHeight = getHeight();

将view的宽、高也保存到一个RectF对象中:

private RectF mTmpRectDst = new RectF();
mTmpRectDst.set(0, 0, viewWidth, viewHeight);

这时候,可以计算出一个从 mTmpRectSrc 到 mTmpRectDst的变换矩阵:

private Matrix mDrawMatrix = new Matrix();
mDrawMatrix.setRectToRect(mTmpRectSrc, mTmpRectDst, Matrix.ScaleToFit.CENTER);

Matrix.scaleToFit.CENTER参数确保图片最后可以:居中全部显示在屏幕内,并且至少x轴或y轴方向占满屏幕。

最后,在onDraw方法中,结合上面到到的mDrawMatrix,即可将drawable画到canvas上:

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawable != null) {
int saveCount = canvas.getSaveCount();
canvas.save();
canvas.concat(mDrawMatrix);
mDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
  • 3 如何获取drawable的实现显示区域

上面讲到了两个RectF区域,和一个matrix。分别是:

mTmpRectSrc: drawable原始矩形区域

mTmpRectDst: view显示矩形区域

mDrawMatrix: 从mTmpRectSrc到mTmpRectDst的变换矩阵

 

如何的到矩阵变换后,图片的实际显示区域?这里要注意,mTmpRectDst并非图片的显示区域,这个矩形区域仅仅是用来限定图片的显示区域,而最终的显示区域并不是他。方法很简单:

private RectF mTranslateRect = new Rect();
mDrawMatrix.mapRect(mTranslateRect, mTmpRectSrc);

然后通过mTranslateRect,就可以获取最终显示图片矩形区域的letf/top/right/bottom

  • 4 识别缩放手势和滑动手势

我们可以自己在onTouchEvent方法中识别出滑动、缩放等手势。但朋友们是否有感觉,这些判断逻辑自己实现起来很繁琐,很难保证逻辑的完美无缺。其实android系统提供了两个很好用的辅助类来帮我们做这些事情:

GestureDetector
ScaleGestureDetector

GestureDetector可以用来识别滑动(scroll),ScaleGestureDetector可以用来识别缩放(scale)

我们来看看如何处理scroll。

首先定一个GestureDetector:

mGestureDetector = new GestureDetector(context, this);

this是指GestureDetector.OnGestureListener的实现类,这里可以用view自己来实现。实现的onScroll方法如下:

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    mDrawMatrix.mapRect(mTranslateRect, mTmpRectSrc);
    distanceX = -distanceX;
    distanceY = -distanceY;
    mDrawMatrix.postTranslate(distanceX, distanceY);
    invalidate();
    return true;
}

直接将接口传给我们的x,y方向偏移量设置到matrix中,然后invalidate,view重新draw出来的drawable就移动了。实际应用中,我们可以先获取drawable的实际显示矩形区域,再根据x,y方向偏移量,计算得出最后的偏移量,来限制不可以将图片划出view显示区域。

 

还差一步,需要在view的onTouchEvent方法中,将MotionEvent事件传给GestureDetector,这样就可以用手指来移动图片了:

@Override
public boolean onTouchEvent(MotionEvent event) {
    mGestureDetector.onTouchEvent(event);
    return true;
}

如果少了这步,GestureDetector根据接触不到MotionEvent,也就无法帮我们识别相关手势。

 

处理缩放几本一样,首先定义detector:

private ScaleGestureDetector mScaleGestureDetector = new ScaleGestureDetector(context, this);

this是ScaleGestureDetector.OnScaleGestureListener的实现类,这里可以用view自己来实现。实现的方法为:

@Override
public boolean onScale(ScaleGestureDetector detector) {
    float factor = detector.getScaleFactor();

    mDrawMatrix.mapRect(mTranslateRect, mTmpRectSrc);

    int width = getWidth();
    float centerX;
    if (factor > 1 && mTranslateRect.right - mTranslateRect.left < width) {
        centerX = mTranslateRect.left * width / (mTranslateRect.left + width - mTranslateRect.right);
    } else {
        centerX = detector.getFocusX();
    }

    int height = getHeight();
    float centerY;
    if (factor > 1 && mTranslateRect.bottom - mTranslateRect.top < height) {
        centerY = mTranslateRect.top * height / (mTranslateRect.top + height - mTranslateRect.bottom);
    } else {
        centerY = detector.getFocusY();
    }
    mDrawMatrix.postScale(factor, factor, centerX, centerY);
    invalidate();
    return true;
}

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
    return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}

首先,onScaleBegin方法要返回true,否则不会回调onScale。在onScale中,可以获取缩放中心点、缩放比例,以及当前图片的实际显示区域,根据实际需求,计算出一个最终的缩放比例、中心点。最后将结果postScale到matrix中,invalidate,view重新draw之后,就可看到缩放的效果

 

最后,需要在view的onTouchEvent方法中,将触摸时间告知scaleDetector。否则,scaleDetector根本接触不到相关事件,不会回调相关方法:

@Override
public boolean onTouchEvent(MotionEvent event) {
    mScaleGestureDetector.onTouchEvent(event);
    mGestureDetector.onTouchEvent(event);
    return true;
}

 

  • 5 总结

通过以上讲解,可以看出只要我们熟悉了matrix、rect的相关用法,Photoviewer的实现技术其实很简单。常见的fling、snap动画效果,photoview中也有实现,读者感兴趣的话,可以查看源码自行研究。

转载于:https://www.cnblogs.com/zc9527/p/5599317.html

PhotoView PhotoView aims to help produce an easily usable implementation of a zooming Android ImageView. Branch Develop: Build Status Branch Master: Build Status PhotoView Features Out of the box zooming, using multi-touch and double-tap. Scrolling, with smooth scrolling fling. Works perfectly when used in a scrolling parent (such as ViewPager). Allows the application to be notified when the displayed Matrix has changed. Useful for when you need to update your UI based on the current zoom/scroll position. Allows the application to be notified when the user taps on the Photo. Sample Application The sample application (the source is in the repository) has been published onto Google Play for easy access: Get it on Google Play Gradle Dependency Add this in your root build.gradle file (not your module build.gradle file): allprojects { repositories { ... maven { url "https://jitpack.io" } } } Then, add the library to your project build.gradle dependencies { compile 'com.github.chrisbanes:PhotoView:1.2.6' } Sample Usage There is a sample provided which shows how to use the library in a more advanced way, but for completeness here is all that is required to get PhotoView working: ImageView mImageView; PhotoViewAttacher mAttacher; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Any implementation of ImageView can be used! mImageView = (ImageView) findViewById(R.id.iv_photo); // Set the Drawable displayed Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper); mImageView.setImageDrawable(bitmap); // Attach a PhotoViewAttacher, which takes care of all of the zooming functionality. // (not needed unless you are going to change the drawable later) mAttacher = new PhotoViewAttacher(mImageView); } // If you later call mImageView.setImageDrawable/setImageBitmap/setImageResource/etc then you just need to call mAttacher.update(); Issues With ViewGroups There are some ViewGroups (ones that utilize onInterceptTouchEvent) that throw exceptions when a PhotoView is placed within them, most notably ViewPager and DrawerLayout. This is a framework issue that has not been resolved. In order to prevent this exception (which typically occurs when you zoom out), take a look at HackyDrawerLayout and you can see the solution is to simply catch the exception. Any ViewGroup which uses onInterceptTouchEvent will also need to be extended and exceptions caught. Use the HackyDrawerLayout as a template of how to do so. The basic implementation is: public class HackyProblematicViewGroup extends ProblematicViewGroup { public HackyProblematicViewGroup(Context context) { super(context); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { try { return super.onInterceptTouchEvent(ev); } catch (IllegalArgumentException e) { //uncomment if you really want to see these errors //e.printStackTrace(); return false; } } } Usage with Fresco Due to the complex nature of Fresco, this library does not currently support Fresco. See this project as an alternative solution. Subsampling Support This library aims to keep the zooming implementation simple. If you are looking for an implementation that supports subsampling, check out this project Pull Requests / Contribution Development happens in develop branch of this repository, and Pull Requests should be filled against that branch. Any Pull Request against master will be rejected License Copyright 2011, 2012 Chris Banes Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值