当前位置: 移动技术网 > IT编程>移动开发>Android > Android实现图片在屏幕内缩放和移动效果

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

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

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

一、需求

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

二、自定义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以上的系统需要动态获取权限。动态获取权限的方法这里就不介绍了,网上有很详细的说明。

五、最终效果

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网