当前位置: 移动技术网 > IT编程>移动开发>Android > Android App中实现可以双击放大和缩小图片功能的实例

Android App中实现可以双击放大和缩小图片功能的实例

2019年07月24日  | 移动技术网IT编程  | 我要评论

珍娜·普雷斯利,裕邦智能法律服务平台,杨幂露点

先来看一个很简单的核心图片缩放方法:

public static bitmap scale(bitmap bitmap, float scalewidth, float scaleheight) { 
  int width = bitmap.getwidth(); 
  int height = bitmap.getheight(); 
  matrix matrix = new matrix(); 
  matrix.postscale(scalewidth, scaleheight); 
  log.i(tag, "scalewidth:"+ scalewidth +", scaleheight:"+ scaleheight); 
  return bitmap.createbitmap(bitmap, 0, 0, width, height, matrix, true); 
} 

注意要比例设置正确否则可能会内存溢出,比如曾经使用图片缩放时遇到这么个问题:

java.lang.illegalargumentexception: bitmap size exceeds 32bits

后来一行行查代码,发现原来是 scale 的比例计算错误,将原图给放大了 20 多倍,导致内存溢出所致,重新修改比例值后就正常了。

好了,下面真正来看一下这个实现了放大和原大两个级别的缩放的模块。
功能有:

  • 以触摸点为中心放大(这个是网上其他的代码没有的)
  • 边界控制(这个是网上其他的代码没有的)
  • 双击放大或缩小(主要考虑到电阻屏)
  • 多点触摸放大和缩小

这个模块已经通过了测试,并且用户也使用有一段时间了,是属于比较稳定的了。

下面贴上代码及使用方法(没有写测试项目,大家见谅):

imagecontrol 类似一个用户自定义的imageview控件。用法将在下面的代码中贴出。

import android.content.context; 
import android.graphics.bitmap; 
import android.graphics.matrix; 
import android.util.attributeset; 
import android.util.floatmath; 
import android.view.motionevent; 
import android.widget.imageview; 
 
public class imagecontrol extends imageview { 
  public imagecontrol(context context) { 
    super(context); 
    // todo auto-generated constructor stub 
  } 
 
  public imagecontrol(context context, attributeset attrs) { 
    super(context, attrs); 
    // todo auto-generated constructor stub 
  } 
 
  public imagecontrol(context context, attributeset attrs, int defstyle) { 
    super(context, attrs, defstyle); 
    // todo auto-generated constructor stub 
  } 
 
  // imageview img; 
  matrix imgmatrix = null; // 定义图片的变换矩阵 
 
  static final int double_click_time_space = 300; // 双击时间间隔 
  static final int double_point_distance = 10; // 两点放大两点间最小间距 
  static final int none = 0; 
  static final int drag = 1; // 拖动操作 
  static final int zoom = 2; // 放大缩小操作 
  private int mode = none; // 当前模式 
 
  float bigscale = 3f; // 默认放大倍数 
  boolean isbig = false; // 是否是放大状态 
  long lastclicktime = 0; // 单击时间 
  float startdistance; // 多点触摸两点距离 
  float enddistance; // 多点触摸两点距离 
 
  float topheight; // 状态栏高度和标题栏高度 
  bitmap primarybitmap = null; 
 
  float contentw; // 屏幕内容区宽度 
  float contenth; // 屏幕内容区高度 
 
  float primaryw; // 原图宽度 
  float primaryh; // 原图高度 
 
  float scale; // 适合屏幕缩放倍数 
  boolean ismovex = true; // 是否允许在x轴拖动 
  boolean ismovey = true; // 是否允许在y轴拖动 
  float startx; 
  float starty; 
  float endx; 
  float endy; 
  float subx; 
  float suby; 
  float limitx1; 
  float limitx2; 
  float limity1; 
  float limity2; 
  icustommethod mcustommethod = null; 
 
  /** 
   * 初始化图片 
   * 
   * @param bitmap 
   *      要显示的图片 
   * @param contentw 
   *      内容区域宽度 
   * @param contenth 
   *      内容区域高度 
   * @param topheight 
   *      状态栏高度和标题栏高度之和 
   */ 
  public void imageinit(bitmap bitmap, int contentw, int contenth, 
      int topheight, icustommethod icustommethod) { 
    this.primarybitmap = bitmap; 
    this.contentw = contentw; 
    this.contenth = contenth; 
    this.topheight = topheight; 
    mcustommethod = icustommethod; 
    primaryw = primarybitmap.getwidth(); 
    primaryh = primarybitmap.getheight(); 
    float scalex = (float) contentw / primaryw; 
    float scaley = (float) contenth / primaryh; 
    scale = scalex < scaley ? scalex : scaley; 
    if (scale < 1 && 1 / scale < bigscale) { 
      bigscale = (float) (1 / scale + 0.5); 
    } 
 
    imgmatrix = new matrix(); 
    subx = (contentw - primaryw * scale) / 2; 
    suby = (contenth - primaryh * scale) / 2; 
    this.setimagebitmap(primarybitmap); 
    this.setscaletype(scaletype.matrix); 
    imgmatrix.postscale(scale, scale); 
    imgmatrix.posttranslate(subx, suby); 
    this.setimagematrix(imgmatrix); 
  } 
 
  /** 
   * 按下操作 
   * 
   * @param event 
   */ 
  public void mousedown(motionevent event) { 
    mode = none; 
    startx = event.getrawx(); 
    starty = event.getrawy(); 
    if (event.getpointercount() == 1) { 
      // 如果两次点击时间间隔小于一定值,则默认为双击事件 
      if (event.geteventtime() - lastclicktime < double_click_time_space) { 
        changesize(startx, starty); 
      } else if (isbig) { 
        mode = drag; 
      } 
    } 
 
    lastclicktime = event.geteventtime(); 
  } 
 
  /** 
   * 非第一个点按下操作 
   * 
   * @param event 
   */ 
  public void mousepointdown(motionevent event) { 
    startdistance = getdistance(event); 
    if (startdistance > double_point_distance) { 
      mode = zoom; 
    } else { 
      mode = none; 
    } 
  } 
 
  /** 
   * 移动操作 
   * 
   * @param event 
   */ 
  public void mousemove(motionevent event) { 
    if ((mode == drag) && (ismovex || ismovey)) { 
      float[] xy = gettranslatexy(imgmatrix); 
      float transx = 0; 
      float transy = 0; 
      if (ismovex) { 
        endx = event.getrawx(); 
        transx = endx - startx; 
        if ((xy[0] + transx) <= limitx1) { 
          transx = limitx1 - xy[0]; 
        } 
        if ((xy[0] + transx) >= limitx2) { 
          transx = limitx2 - xy[0]; 
        } 
      } 
      if (ismovey) { 
        endy = event.getrawy(); 
        transy = endy - starty; 
        if ((xy[1] + transy) <= limity1) { 
          transy = limity1 - xy[1]; 
        } 
        if ((xy[1] + transy) >= limity2) { 
          transy = limity2 - xy[1]; 
        } 
      } 
 
      imgmatrix.posttranslate(transx, transy); 
      startx = endx; 
      starty = endy; 
      this.setimagematrix(imgmatrix); 
    } else if (mode == zoom && event.getpointercount() > 1) { 
      enddistance = getdistance(event); 
      float dif = enddistance - startdistance; 
      if (math.abs(enddistance - startdistance) > double_point_distance) { 
        if (isbig) { 
          if (dif < 0) { 
            changesize(0, 0); 
            mode = none; 
          } 
        } else if (dif > 0) { 
          float x = event.getx(0) / 2 + event.getx(1) / 2; 
          float y = event.gety(0) / 2 + event.gety(1) / 2; 
          changesize(x, y); 
          mode = none; 
        } 
      } 
    } 
  } 
 
  /** 
   * 鼠标抬起事件 
   */ 
  public void mouseup() { 
    mode = none; 
  } 
 
  /** 
   * 图片放大缩小 
   * 
   * @param x 
   *      点击点x坐标 
   * @param y 
   *      点击点y坐标 
   */ 
  private void changesize(float x, float y) { 
    if (isbig) { 
      // 如果处于最大状态,则还原 
      imgmatrix.reset(); 
      imgmatrix.postscale(scale, scale); 
      imgmatrix.posttranslate(subx, suby); 
      isbig = false; 
    } else { 
      imgmatrix.postscale(bigscale, bigscale); // 在原有矩阵后乘放大倍数 
      float transx = -((bigscale - 1) * x); 
      float transy = -((bigscale - 1) * (y - topheight)); // (bigscale-1)(y-statusbarheight-suby)+2*suby; 
      float currentwidth = primaryw * scale * bigscale; // 放大后图片大小 
      float currentheight = primaryh * scale * bigscale; 
      // 如果图片放大后超出屏幕范围处理 
      if (currentheight > contenth) { 
        limity1 = -(currentheight - contenth); // 平移限制 
        limity2 = 0; 
        ismovey = true; // 允许在y轴上拖动 
        float currentsuby = bigscale * suby; // 当前平移距离 
        // 平移后,内容区域上部有空白处理办法 
        if (-transy < currentsuby) { 
          transy = -currentsuby; 
        } 
        // 平移后,内容区域下部有空白处理办法 
        if (currentsuby + transy < limity1) { 
          transy = -(currentheight + currentsuby - contenth); 
        } 
      } else { 
        // 如果图片放大后没有超出屏幕范围处理,则不允许拖动 
        ismovey = false; 
      } 
 
      if (currentwidth > contentw) { 
        limitx1 = -(currentwidth - contentw); 
        limitx2 = 0; 
        ismovex = true; 
        float currentsubx = bigscale * subx; 
        if (-transx < currentsubx) { 
          transx = -currentsubx; 
        } 
        if (currentsubx + transx < limitx1) { 
          transx = -(currentwidth + currentsubx - contentw); 
        } 
      } else { 
        ismovex = false; 
      } 
 
      imgmatrix.posttranslate(transx, transy); 
      isbig = true; 
    } 
 
    this.setimagematrix(imgmatrix); 
    if (mcustommethod != null) { 
      mcustommethod.custommethod(isbig); 
    } 
  } 
 
  /** 
   * 获取变换矩阵中x轴偏移量和y轴偏移量 
   * 
   * @param matrix 
   *      变换矩阵 
   * @return 
   */ 
  private float[] gettranslatexy(matrix matrix) { 
    float[] values = new float[9]; 
    matrix.getvalues(values); 
    float[] floats = new float[2]; 
    floats[0] = values[matrix.mtrans_x]; 
    floats[1] = values[matrix.mtrans_y]; 
    return floats; 
  } 
 
  /** 
   * 获取两点间的距离 
   * 
   * @param event 
   * @return 
   */ 
  private float getdistance(motionevent event) { 
    float x = event.getx(0) - event.getx(1); 
    float y = event.gety(0) - event.gety(1); 
    return floatmath.sqrt(x * x + y * y); 
  } 
 
  /** 
   * @author administrator 用户自定义方法 
   */ 
  public interface icustommethod { 
    public void custommethod(boolean currentstatus); 
  } 
} 

 

imagevewactivity 这个用于测试的activity

import android.app.activity; 
import android.graphics.bitmap; 
import android.graphics.rect; 
import android.graphics.drawable.bitmapdrawable; 
import android.os.bundle; 
import android.view.motionevent; 
import android.view.view; 
import android.widget.linearlayout; 
import android.widget.textview; 
import android.widget.toast; 
import ejiang.boiler.imagecontrol.icustommethod; 
import ejiang.boiler.r.id; 
 
public class imageviewactivity extends activity { 
 
  @override 
  protected void oncreate(bundle savedinstancestate) { 
    // todo auto-generated method stub 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.common_image_view); 
    findview(); 
  } 
 
  public void onwindowfocuschanged(boolean hasfocus) { 
    super.onwindowfocuschanged(hasfocus); 
    init(); 
  } 
 
  imagecontrol imgcontrol; 
  linearlayout lltitle; 
  textview tvtitle; 
 
  private void findview() { 
    imgcontrol = (imagecontrol) findviewbyid(id.common_imageview_imagecontrol1); 
    lltitle = (linearlayout) findviewbyid(id.common_imageview_lltitle); 
    tvtitle = (textview) findviewbyid(id.common_imageview_title); 
  } 
 
  private void init() { 
    tvtitle.settext("图片测试"); 
    // 这里可以为imgcontrol的图片路径动态赋值 
    // ............ 
     
    bitmap bmp; 
    if (imgcontrol.getdrawingcache() != null) { 
      bmp = bitmap.createbitmap(imgcontrol.getdrawingcache()); 
    } else { 
      bmp = ((bitmapdrawable) imgcontrol.getdrawable()).getbitmap(); 
    } 
    rect frame = new rect(); 
    getwindow().getdecorview().getwindowvisibledisplayframe(frame); 
    int statusbarheight = frame.top; 
    int screenw = this.getwindowmanager().getdefaultdisplay().getwidth(); 
    int screenh = this.getwindowmanager().getdefaultdisplay().getheight() 
        - statusbarheight; 
    if (bmp != null) { 
      imgcontrol.imageinit(bmp, screenw, screenh, statusbarheight, 
          new icustommethod() { 
            
            @override 
            public void custommethod(boolean currentstatus) { 
              // 当图片处于放大或缩小状态时,控制标题是否显示 
              if (currentstatus) { 
                lltitle.setvisibility(view.gone); 
              } else { 
                lltitle.setvisibility(view.visible); 
              } 
            } 
          }); 
    } 
    else 
    { 
      toast.maketext(imageviewactivity.this, "图片加载失败,请稍候再试!", toast.length_short) 
          .show(); 
    } 
 
  } 
 
  @override 
  public boolean ontouchevent(motionevent event) { 
    switch (event.getaction() & motionevent.action_mask) { 
    case motionevent.action_down: 
      imgcontrol.mousedown(event);       
      break; 
 
    /** 
     * 非第一个点按下 
     */ 
    case motionevent.action_pointer_down: 
     
        imgcontrol.mousepointdown(event); 
     
      break; 
    case motionevent.action_move: 
        imgcontrol.mousemove(event); 
       
      break; 
 
    case motionevent.action_up: 
      imgcontrol.mouseup(); 
      break; 
 
    } 
 
    return super.ontouchevent(event); 
  } 
} 

        在上面的代码中,需要注意两点。一activity中要重写ontouchevent方法,将触摸事件传递到imagecontrol,这点类似于wpf中的路由事件机制。二初始化imgcontrol即imgcontrol.imageinit,注意其中的参数。最后一个参数类似于c#中的委托,我这里使用接口来实现,在放大缩小的切换时要执行的操作都卸载这个方法中。


common_image_view.xml  布局文件

<?xml version="1.0" encoding="utf-8"?> 
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:id="@+id/rl" 
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" > 
 
  <ejiang.boiler.imagecontrol 
    android:id="@+id/common_imageview_imagecontrol1" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:src="@drawable/ic_launcher" /> 
 
  <linearlayout 
    android:id="@+id/common_imageview_lltitle" 
    style="@style/reporttitle1" 
    android:layout_alignparentleft="true" 
    android:layout_alignparenttop="true" > 
 
    <textview 
      android:id="@+id/common_imageview_title" 
      style="@style/title2" 
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:layout_weight="1" 
      android:text="报告" /> 
  </linearlayout> 
 
</relativelayout> 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网