首页 > 系统 > Android > 正文

Android实现图片在屏幕内缩放和移动效果

2019-10-21 21:25:50
字体:
来源:转载
供稿:网友

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

一、需求

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

Android,图片,屏幕,缩放,移动

二、自定义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,单位kb private 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; } }  @Override protected 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(); } }  @Override public 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; }  @Override protected 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.ZoomImageView android: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,图片,屏幕,缩放,移动

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表