Android PhotoView基本功能实现
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中也有实现,读者感兴趣的话,可以查看源码自行研究。
Android PhotoView基本功能实现的更多相关文章
- Android Studio调试功能使用总结【转】
Android Studio调试功能使用总结[转] 这段时间一直在使用Intellij IDEA, 今天把调试区工具的使用方法记录于此. 先编译好要调试的程序. 1.设置断点 选定要设置断点的代码 ...
- Android 实现闹钟功能
原文地址:Android 实现闹钟功能作者:Android_Learners 一.手机闹钟主要用到了AlarmManager类,AlarmManager类提供了访问系统定时服务的途径,开发人员可以 ...
- Android Studio调试功能使用总结---转
Android Studio调试功能使用总结[转] 这段时间一直在使用Intellij IDEA, 今天把调试区工具的使用方法记录于此. 先编译好要调试的程序. 1.设置断点 选定要设置断点的代码 ...
- 集成Android免费语音合成功能(在线、离线、离在线融合)
集成Android免费语音合成功能(在线.离线.离在线融合),有这一篇文章就够了(离线)集成Android免费语音合成功能(在线.离线.离在线融合),有这一篇文章就够了(离在线融合) 转眼间,大半年没 ...
- Android中选项卡功能的实现
Android中选项卡功能的实现 Android中使用TabHost和TabWidget来实现选项卡功能.TabHost必须是布局的根节点,它包含两个子节点: TabWidget,显示选项卡: Fra ...
- Android社会化分享功能的实现步骤
众所周知,互联网是一个资源共享的地方,在网络上,我们可以分享我们所有认为好的资源.而随着互联网信息爆发式的增长,我们习惯了一键分享功能,比如:微博分享.微信分享.QQ空间分享.人人网分享等等.由此可见 ...
- Android微信分享功能实例+demo
Android微信分享功能实例 1 微信开放平台注册 2 获得appId,添加到程序中,并运行程序 3 使用应用签名apk生成签名,添加到微信开放平台应用签名,完成注册 4 测试分享功能. 有问题请留 ...
- (转载) Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框
Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框 标签: Android清除功能EditText仿IOS的输入框 2013-09-04 17:33 70865人阅读 ...
- Android 调用摄像头功能【拍照与视频】
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/ma_hoking/article/details/28292973 应用场景: 在Android开发 ...
随机推荐
- Spring Boot 快速入门 史上最简单
1.Spring Boot 概述 Spring Boot 是所有基于 Spring 开发的项目的起点.Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的 ...
- DZY Loves Math II:多重背包dp+组合数
Description Input 第一行,两个正整数 S 和 q,q 表示询问数量.接下来 q 行,每行一个正整数 n. Output 输出共 q 行,分别为每个询问的答案. Sample Inpu ...
- CSPS模拟 46
勿忘国耻. 由于重新评测我看到了不是很真实的一幕 紧接着是更不真实的一幕 就在虚假形象快要建立完成的时候 它由于来自东方的神秘力量倒塌了 被两个学校的大佬爆踩了(捂脸 T1 无脑背包? 考试时想1h想 ...
- Hive数据仓库你了解了吗
在工作中我们经常使用的数据库,数据库一般存放的我们系统中常用的数据,一般为百万级别.如果数据量庞大,达到千万级.亿级又需要对他们进行关联运算,该怎么办呢? 前面我们已经介绍了HDFS和MapReduc ...
- What's your name?
Hello. My name is james. What's your name? Hi, I'm Jessica. Nice to meet you. Nice to meet you, too. ...
- linux shell脚本语法笔记
1.&,&&,|,|| &:除了最后一个cmd,前面的cmd均已后台方式静默执行,执行结果显示在终端上,个别的cmd错误不影响整个命令的执行,全部的cmd同时执行 &a ...
- Python连接SqlServer+GUI嵌入式——学生管理系统1.0
学生管理系统1.0 1.建学生数据库 2.数据库嵌入高级语言(Python) 3.界面设计 简化思路: 1.先通过SqlServer2012建立学生数据库,包括账号.密码,姓名.选课等信息 2.运用P ...
- nyoj 455-黑色帽子
455-黑色帽子 内存限制:64MB 时间限制:1000ms 特判: No 通过数:4 提交数:7 难度:1 题目描述: 最近发现了一个搞笑的游戏,不过目前还没玩过.一个舞会上,每个人 ...
- Bootstrap——导航条(navbar)
导航条和导航从外观上差别不是太多,但在实际使用中导航条要比导航复杂得多. 导航条(navbar)中有一个背景色.而且导航条可以是纯链接(类似导航).表单以及表单和导航一起结合等多种形式. 在制作一个基 ...
- 关于GC(中):Java垃圾回收相关基础知识
Java内存模型 (图源: 深入理解JVM-内存模型(jmm)和GC) 区域名 英文名 访问权限 作用 备注 程序计数器 Program Counter Register 线程隔离 标记待取的下一条执 ...