通常我们遇到的图片缩放需求,都是图片基于屏幕自适应后,进行缩放和移动,且图片最小只能是自适应的大小。最近遇到一个需求,要求图片只能在屏幕内缩放和移动,不能超出屏幕。

一、需求

在屏幕中加载一张图片,图片可以手势缩放移动。但是图片最大只能缩放到屏幕大小,也只允许在屏幕内移动。可以从系统中读取图片(通过绝对路径),也可以从资源文件中读取图片。

二、自定义ZoomImageView

屏幕内手势缩放图片与普通的图片缩放相比,比较麻烦的是,需要计算图片的精确位置。不同于普通缩放的图片充满屏幕,屏内缩放的图片只占据屏幕的一部分,我们需要判断手指是否点在图片内,才能进行各种操作。

/*** 判断手指是否点在图片内(单指)*/private void isClickInImage(){if (translationX <= mFirstX && mFirstX <= (translationX + currentW)&& translationY <= mFirstY && mFirstY <= (translationY + currentH)){isClickInImage = true;}else {isClickInImage = false;}}/*** 判断手指是否点在图片内(双指)* 只要有一只手指在图片内就为true* @param event*/private void isClickInImage(MotionEvent event){if (translationX <= event.getX(0) && event.getX(0) <= (translationX + currentW)&& translationY <= event.getY(0) && event.getY(0) <= (translationY + currentH)){isClickInImage = true;}else if (translationX <= event.getX(1) && event.getX(1) <= (translationX + currentW)&& translationY <= event.getY(1) && event.getY(1) <= (translationY + currentH)){isClickInImage = true;}else {isClickInImage = false;}}

其他的各种操作,之于缩放,移动,边界检查等,和普通的图片缩放没有太多区别。完整代码如下:

package com.uni.myapplication;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;import java.io.File;/*** Created by newcboy on 2018/3/9.*/public class ZoomImageView extends View {public static final int IMAGE_MAX_SIZE = 1000;//加载图片允许的最大size,单位kbprivate float minimal = 100.0f;private float screenW;//屏幕宽度private float screenH;//屏幕高度//单指按下的坐标private float mFirstX = 0.0f;private float mFirstY = 0.0f;//单指离开的坐标private float lastMoveX =-1f;private float lastMoveY =-1f;//两指的中点坐标private float centPointX;private float centPointY;//图片的绘制坐标private float translationX = 0.0f;private float translationY = 0.0f;//图片的原始宽高private float primaryW;private float primaryH;//图片当前宽高private float currentW;private float currentH;private float scale = 1.0f;private float maxScale, minScale;private Bitmap bitmap;private Matrix matrix;private int mLocker = 0;private float fingerDistance = 0.0f;private boolean isLoaded = false;private boolean isClickInImage = false;public ZoomImageView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}/*** 从资源文件中读取图片* @param context* @param imageId*/public void setResourceBitmap(Context context, int imageId){bitmap = BitmapFactory.decodeResource(context.getResources(), imageId);isLoaded = true;primaryW = bitmap.getWidth();primaryH = bitmap.getHeight();matrix = new Matrix();}/*** 根据路径添加图片* @param path* @param scale*/public void setImagePathBitmap(String path, float scale){this.scale = scale;setImageBitmap(path);}private void setImageBitmap(String path){File file = new File(path);if (file.exists()){isLoaded = true;bitmap = ImageLoadUtils.getImageLoadBitmap(path, IMAGE_MAX_SIZE);primaryW = bitmap.getWidth();primaryH = bitmap.getHeight();matrix = new Matrix();}else {isLoaded = false;}}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);if (changed){screenW = getWidth();screenH = getHeight();translationX = (screenW - bitmap.getWidth() * scale)/  2;translationY = (screenH - bitmap.getHeight() * scale) / 2;setMaxMinScale();}}/****/private void setMaxMinScale(){float xScale, yScale;xScale = minimal / primaryW;yScale = minimal / primaryH;minScale = xScale > yScale ? xScale : yScale;xScale = primaryW / screenW;yScale = primaryH / screenH;if (xScale > 1 || yScale > 1 ) {if (xScale > yScale) {maxScale = 1/xScale;}else {maxScale = 1/yScale;}}else {if (xScale > yScale) {maxScale = 1/xScale;}else {maxScale = 1/yScale;}}if (isScaleError()){restoreAction();}}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (!isLoaded){return true;}switch (event.getActionMasked()){case MotionEvent.ACTION_DOWN:mFirstX = event.getX();mFirstY = event.getY();isClickInImage();break;case MotionEvent.ACTION_POINTER_DOWN:fingerDistance = getFingerDistance(event);isClickInImage(event);break;case MotionEvent.ACTION_MOVE:float fingerNum = event.getPointerCount();if (fingerNum == 1 && mLocker == 0 && isClickInImage){movingAction(event);}else if (fingerNum == 2 && isClickInImage){zoomAction(event);}break;case MotionEvent.ACTION_POINTER_UP:mLocker = 1;if (isScaleError()){translationX = (event.getX(1) + event.getX(0)) / 2;translationY = (event.getY(1) + event.getY(0)) / 2;}break;case MotionEvent.ACTION_UP:lastMoveX = -1;lastMoveY = -1;mLocker = 0;if (isScaleError()){restoreAction();}break;}return true;}/*** 移动操作* @param event*/private void movingAction(MotionEvent event){float moveX = event.getX();float moveY = event.getY();if (lastMoveX == -1 || lastMoveY == -1) {lastMoveX = moveX;lastMoveY = moveY;}float moveDistanceX = moveX - lastMoveX;float moveDistanceY = moveY - lastMoveY;translationX = translationX + moveDistanceX;translationY = translationY + moveDistanceY;lastMoveX = moveX;lastMoveY = moveY;invalidate();}/*** 缩放操作* @param event*/private void zoomAction(MotionEvent event){midPoint(event);float currentDistance = getFingerDistance(event);if (Math.abs(currentDistance - fingerDistance) > 1f) {float moveScale = currentDistance / fingerDistance;scale = scale * moveScale;translationX = translationX * moveScale + centPointX * (1-moveScale);translationY = translationY * moveScale + centPointY * (1-moveScale);fingerDistance = currentDistance;invalidate();}}/*** 图片恢复到指定大小*/private void restoreAction(){if (scale < minScale){scale  = minScale;}else if (scale > maxScale){scale = maxScale;}translationX = translationX - bitmap.getWidth()*scale / 2;translationY = translationY - bitmap.getHeight()*scale / 2;invalidate();}/*** 判断手指是否点在图片内(单指)*/private void isClickInImage(){if (translationX <= mFirstX && mFirstX <= (translationX + currentW)&& translationY <= mFirstY && mFirstY <= (translationY + currentH)){isClickInImage = true;}else {isClickInImage = false;}}/*** 判断手指是否点在图片内(双指)* 只要有一只手指在图片内就为true* @param event*/private void isClickInImage(MotionEvent event){if (translationX <= event.getX(0) && event.getX(0) <= (translationX + currentW)&& translationY <= event.getY(0) && event.getY(0) <= (translationY + currentH)){isClickInImage = true;}else if (translationX <= event.getX(1) && event.getX(1) <= (translationX + currentW)&& translationY <= event.getY(1) && event.getY(1) <= (translationY + currentH)){isClickInImage = true;}else {isClickInImage = false;}}/*** 获取两指间的距离* @param event* @return*/private float getFingerDistance(MotionEvent event){float x = event.getX(1) - event.getX(0);float y = event.getY(1) - event.getY(0);return (float) Math.sqrt(x * x + y * y);}/*** 判断图片大小是否符合要求* @return*/private boolean isScaleError(){if (scale > maxScale|| scale < minScale){return true;}return false;}/*** 获取两指间的中点坐标* @param event*/private void midPoint(MotionEvent event){centPointX = (event.getX(1) + event.getX(0))/2;centPointY = (event.getY(1) + event.getY(0))/2;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (isLoaded){imageZoomView(canvas);}}private void imageZoomView(Canvas canvas){currentW = primaryW * scale;currentH = primaryH * scale;matrix.reset();matrix.postScale(scale, scale);//x轴y轴缩放peripheryJudge();matrix.postTranslate(translationX, translationY);//中点坐标移动canvas.drawBitmap(bitmap, matrix, null);}/*** 图片边界检查* (只在屏幕内)*/private void peripheryJudge(){if (translationX < 0){translationX = 0;}if (translationY < 0){translationY = 0;}if ((translationX + currentW) > screenW){translationX = screenW - currentW;}if ((translationY + currentH) > screenH){translationY = screenH - currentH;}}}

实际上,用Bitmap绘制图片时,可以通过Paint设置图片透明度。

Paint paint = new Paint();
paint.setStyle( Paint.Style.STROKE);
paint.setAlpha(150);

在setAlpha()中传入一个0~255的整数。数字越大,透明度越低。

然后在绘制图片时

canvas.drawBitmap(bitmap, matrix, paint);

三、ImageLoadUtils图片加载类

这个类是对传入的图片进行压缩处理的类,在应用从系统中读取图片时用到。在写这个类时,发现一些和网上说法不一样的地方。

options.inSampleSize这个属性,网上的说法是必须是2的幂次方,但实际上,我的验证结果是所有的整数都可以。

这里采用的压缩方法是,获取系统剩余内存和图片大小,然后将图片压缩到合适的大小。

package com.uni.myapplication;import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.net.Uri;import java.io.File;
import java.io.FileInputStream;/*** 图片加载工具类** Created by newcboy on 2018/1/25.*/public class ImageLoadUtils {/*** 原图加载,根据传入的指定图片大小。* @param imagePath* @param maxSize* @return*/public static Bitmap getImageLoadBitmap(String imagePath, int maxSize){int fileSize = 1;Bitmap bitmap = null;int simpleSize = 1;File file = new File(imagePath);if (file.exists()) {Uri imageUri = Uri.parse(imagePath);try {fileSize = (int) (getFileSize(file) / 1024);} catch (Exception e) {e.printStackTrace();}Options options = new Options();if (fileSize > maxSize){for (simpleSize = 2; fileSize>= maxSize; simpleSize++){fileSize = fileSize / simpleSize;}}options.inSampleSize = simpleSize;bitmap = BitmapFactory.decodeFile(imageUri.getPath(), options);}return bitmap;}/*** 获取指定文件的大小* @param file* @return* @throws Exception*/public static long getFileSize(File file) throws Exception{if(file == null) {return 0;}long size = 0;if(file.exists()) {FileInputStream mInputStream = new FileInputStream(file);size = mInputStream.available();}return size;}/*** 获取手机运行内存* @param context* @return*/public static long getTotalMemorySize(Context context){long size = 0;ActivityManager activityManager = (ActivityManager) context.getSystemService(context.ACTIVITY_SERVICE);ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();//outInfo对象里面包含了内存相关的信息activityManager.getMemoryInfo(outInfo);//把内存相关的信息传递到outInfo里面C++思想//size = outInfo.totalMem;  //总内存size = outInfo.availMem;    //剩余内存return (size/1024/1024);}}

四、调用

使用方法和通常的控件差不多,只是多了一个设置图片的方法。

1.在布局文件中添加布局。

<com.uni.myapplication.ZoomImageViewandroid:id="@+id/zoom_image_view"android:layout_width="wrap_content"android:layout_height="wrap_content" />

2.在代码中调用

zoomImageView = (ZoomImageView) findViewById(R.id.zoom_image_view);
zoomImageView.setImagePathBitmap(MainActivity.this, imagePath, 1.0f);
zoomImageView.setResourceBitmap(MainActivity.this, R.mipmap.ic_launcher);
其中setImagePathBitmap()是从系统中读取图片加载的方法,setResourceBitmap()是从资源文件中读取图片的方法。

当然,从系统读取图片需要添加读写权限,这个不能忘了。而且6.0以上的系统需要动态获取权限。动态获取权限的方法这里就不介绍了,网上有很详细的说明。

五、最终效果

Android控制图片在屏幕内缩放和移动相关推荐

  1. 微信小程序图片根据屏幕比例缩放

    // 图片根据屏幕比例缩放imageUtil(e) { let self = this,imageSize = {},originalWidth = e.detail.width,//图片原始宽 or ...

  2. Android matrix 控制图片的旋转、缩放、移动

    本文主要讲解利用android中Matrix控制图形的旋转缩放移动,具体参见一下代码: /** * 使用矩阵控制图片移动.缩放.旋转 * @author 张进 */ public class Comm ...

  3. android matrix 实现点击旋转,Android中利用matrix 控制图片的旋转、缩放、移动

    本文主要讲解利用android中Matrix控制图形的旋转缩放移动,具体参见一下代码: /** * 使用矩阵控制图片移动.缩放.旋转 */ public class CommonImgEffectVi ...

  4. Android之matrix类控制图片的旋转、缩放、移动

    在Android中,对图片的处理需要使用到Matrix类,Matrix是一个3 x 3的矩阵,他对图片的处理分为四个基本类型: 1.Translate----平移变换 2.Scale----缩放变换 ...

  5. android 背景图片自适应屏幕大小,android 设置桌面背景图片适应屏幕大小

    今天做demo,设置桌面每天自动更新背景,但是桌面的背景都是被系统裁剪过的图片,上网百度了一下,有个过时的方法,改进后果然有用了! Bitmap bmp=BitmapFactory.decodeRes ...

  6. android 手势放缩_Android应用中实现手势控制图片缩放的完全攻略

    一.概述现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~ 我相信看图的整个步骤,大家或者说用户应 ...

  7. android 手势事件 重写,Android实现通过手势控制图片大小缩放的方法

    本文实例讲述了Android实现通过手势控制图片大小缩放的方法.分享给大家供大家参考,具体如下: 该程序实现的是通过手势来缩放图片,从左向右挥动图片时图片被放大,从右向左挥动图片时图片被缩小,挥动速度 ...

  8. Android ImageView图片代码实现按屏幕宽度等比例缩放

    /*** 设置图片根据屏幕宽度进行等比例缩放* @param imageView*/public static void setImageMatchScreenWidth(ImageView imag ...

  9. android listview 只加载显示的图片大小,Android ListView只加载当前屏幕内的图片(解决list滑动时加载卡顿)...

    最近在做ListView分页显示,其中包括图片 和文字(先下载解析文字内容,再异步加载图片)发现每次点击下一页后,文字内容加载完毕,马上向下滑动,由于这时后台在用线程池异步下载图片,我每页有20条,也 ...

最新文章

  1. Pandas缺失数据
  2. win8.1系统用户看过来,0x80004005错误解决大法!
  3. libgdx和unity_libgdx和Kotlin –类[2D平台原型]
  4. 推一波JAVA学习公众号
  5. 西瓜书+实战+吴恩达机器学习(四)监督学习之线性回归 Linear Regression
  6. 53 岁张亚勤官宣:正式加入清华!
  7. 01-bilibilidemo配置
  8. 错误使用 eig 输入矩阵包含 nan 或 inf_特斯拉AI主管提醒你神经网络的几大常见错误...
  9. 单片机原理及应用程序c语言版题库,单片机原理及应用期末复习题库(含答案)...
  10. 视频录制工具OBS选择区域录制
  11. 2022年阿里云服务器租用价格表(最新收费标准及活动价格表)
  12. hd4600黑苹果html5死机,HD4600终于可以不花屏了,写下心得。
  13. php微缩图,PHP进阶技巧:php用流方式制作缩略图
  14. 笔记本显示器根据页面显示亮度自动调节亮度的问题
  15. IIP3/IMD/IM3/P1dB
  16. 5个免费翻译网页的工具,快速 、高效、准确率高!
  17. 不用自学APP开发:零基础也能制作APP软件,兼容iOS
  18. IIS 7.5 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理。
  19. docker部署Harbor
  20. 点阵图dotplot参数详解

热门文章

  1. 深圳软件测试培训:Postman的Monitor功能
  2. 完成自己的Google测试框架
  3. 每日一记 - 3.7
  4. cat 021 解析
  5. 银行应构建主动式客户体验管理体系,助力客户价值增长
  6. Shopee账户被冻结是什么原因?怎么解封?
  7. mac服务器文件同步软件,[MACOS]使用fswatch和SCP配合实现自动单向实时同步文件
  8. 运用Acronis True Image恢复系统
  9. java基础学习—— 六
  10. 第十一课 模糊工具、海绵工具、仿制图章工具