欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-03-08 17:15:17
通常我们遇到的图片缩放需求,都是图片基于屏幕自适应后,进行缩放和移动,且图片最小只能是自适应的大小。最近遇到一个需求,要求图片只能在屏幕内缩放和移动,不能超出屏幕。 一、...

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

一、需求

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

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实现图片在屏幕内缩放和移动效果

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