热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

基于Android实现图片平移、缩放、旋转同时进行

这篇文章主要介绍了基于Android实现图片平移、缩放、旋转同时进行的相关资料,需要的朋友可以参考下

前言

之前因为项目需求,其中使用到了图片的单击显示取消,图片平移缩放功能,昨天突然想再加上图片的旋转功能,在网上看了很多相关的例子,可是没看到能同时实现我想要的功能的。

需求:

(1)图片平移、缩放、旋转等一系列操作后,图片需要自动居中显示。

(2)图片旋转后选自动水平显示或者垂直显示

(3)图片在放大缩小的同时都能旋转

Demo实现部分效果截图

Demo主要代码

Java

MainActivity.java
package com.practice.noyet.rotatezoomimageview;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import com.ypy.eventbus.EventBus;
import java.io.File;
import java.math.BigDecimal;
/**
* package: com.practice.noyet.rotatezoomimageview
* Created by noyet on 2015/11/11.
*/
public class MainActivity extends Activity implements View.OnTouchListener {
  private ImageView mImageView;
  private PointF point0 = new PointF();
  private PointF pointM = new PointF();
  private final int NOnE= 0;
  /**
   * 平移
   */
  private final int DRAG = 1;
  /**
   * 旋转、缩放
   */
  private final int ZOOM = 2;
  /**
   * 设定事件模式
   */
  private int mode = NONE;
  /**
   * 图片缩放矩阵
   */
  private Matrix matrix = new Matrix();
  /**
   * 保存触摸前的图片缩放矩阵
   */
  private Matrix savedMatrix = new Matrix();
  /**
   * 保存触点移动过程中的图片缩放矩阵
   */
  private Matrix matrix1 = new Matrix();
  /**
   * 屏幕高度
   */
  private int displayHeight;
  /**
   * 屏幕宽度
   */
  private int displayWidth;
  /**
   * 最小缩放比例
   */
  protected float minScale = 1f;
  /**
   * 最大缩放比例
   */
  protected float maxScale = 3f;
  /**
   * 当前缩放比例
   */
  protected float currentScale = 1f;
  /**
   * 多点触摸2个触摸点间的起始距离
   */
  private float oldDist;
  /**
   * 多点触摸时图片的起始角度
   */
  private float oldRotation = 0;
  /**
   * 旋转角度
   */
  protected float rotation = 0;
  /**
   * 图片初始宽度
   */
  private int imgWidth;
  /**
   * 图片初始高度
   */
  private int imgHeight;
  /**
   * 设置单点触摸退出图片显示时,单点触摸的灵敏度(可针对不同手机单独设置)
   */
  protected final int MOVE_MAX = 2;
  /**
   * 单点触摸时手指触发的‘MotionEvent.ACTION_MOVE'次数
   */
  private int fingerNumMove = 0;
  private Bitmap bm;
  /**
   * 保存matrix缩放比例
   */
  private float matrixScale= 1;
  /*private String imagePath;*/
  /**
   * 显示被存入缓存中的网络图片
   *
   * @param event 观察者事件
   */
  public void onEventMainThread(CustomEventBus event) {
    if (event == null) {
      return;
    }
    if (event.type == CustomEventBus.EventType.SHOW_PICTURE) {
      bm = (Bitmap) event.obj;
      showImage();
    }
  }
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initData();
  }
  public void initData() {
    // TODO Auto-generated method stub
    bm = BitmapFactory.decodeResource(getResources(), R.drawable.alipay);
    DisplayMetrics dm = getResources().getDisplayMetrics();
    displayWidth = dm.widthPixels;
    displayHeight = dm.heightPixels;
    mImageView = (ImageView) findViewById(R.id.image_view);
    mImageView.setOnTouchListener(this);
    showImage();
    //显示网络图片时使用
    /*File file = MainApplication.getInstance().getImageCache()
        .getDiskCache().get(图片路径);
    if (!file.exists()) {
      Toast.makeText(this, "图片路径错误", Toast.LENGTH_SHORT).show();
    } else {
      new MyTask().execute(file);
    }*/
  }
  @Override
  public boolean onTouch(View view, MotionEvent event) {
    ImageView imageView = (ImageView) view;
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
        savedMatrix.set(matrix);
        point0.set(event.getX(), event.getY());
        mode = DRAG;
        System.out.println("MotionEvent--ACTION_DOWN");
        break;
      case MotionEvent.ACTION_POINTER_DOWN:
        oldDist = spacing(event);
        oldRotation = rotation(event);
        savedMatrix.set(matrix);
        setMidPoint(pointM, event);
        mode = ZOOM;
        System.out.println("MotionEvent--ACTION_POINTER_DOWN---" + oldRotation);
        break;
      case MotionEvent.ACTION_UP:
        if (mode == DRAG & (fingerNumMove this.finish();
        }
        checkView();
        centerAndRotate();
        imageView.setImageMatrix(matrix);
        System.out.println("MotionEvent--ACTION_UP");
        fingerNumMove = 0;
        break;
      case MotionEvent.ACTION_POINTER_UP:
        mode = NONE;
        System.out.println("MotionEvent--ACTION_POINTER_UP");
        break;
      case MotionEvent.ACTION_MOVE:
        operateMove(event);
        imageView.setImageMatrix(matrix1);
        fingerNumMove++;
        System.out.println("MotionEvent--ACTION_MOVE");
        break;
    }
    return true;
  }
  @Override
  protected void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    if (bm != null & !bm.isRecycled()) {
      bm.recycle(); // 回收图片所占的内存
      System.gc(); // 提醒系统及时回收
    }
  }
  /**
   * 显示图片
   */
  private void showImage() {
    imgWidth = bm.getWidth();
    imgHeight = bm.getHeight();
    mImageView.setImageBitmap(bm);
    matrix.setScale(1, 1);
    centerAndRotate();
    mImageView.setImageMatrix(matrix);
  }
  /**
   * 触点移动时的操作
   *
   * @param event 触摸事件
   */
  private void operateMove(MotionEvent event) {
    matrix1.set(savedMatrix);
    switch (mode) {
      case DRAG:
        matrix1.postTranslate(event.getX() - point0.x, event.getY() - point0.y);
        break;
      case ZOOM:
        rotation = rotation(event) - oldRotation;
        float newDist = spacing(event);
        float scale = newDist / oldDist;
        currentScale = (scale > 3.5f) ? 3.5f : scale;
        System.out.println("缩放倍数---" + currentScale);
        System.out.println("旋转角度---" + rotation);
        /** 縮放 */
        matrix1.postScale(currentScale, currentScale, pointM.x, pointM.y);
        /** 旋轉 */
        matrix1.postRotate(rotation, displayWidth / 2, displayHeight / 2);
        break;
    }
  }
  /**
   * 两个触点的距离
   *
   * @param event 触摸事件
   * @return float
   */
  private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return (float) Math.sqrt(x * x + y * y);
  }
  /**
   * 获取旋转角度
   */
  private float rotation(MotionEvent event) {
    double delta_x = (event.getX(0) - event.getX(1));
    double delta_y = (event.getY(0) - event.getY(1));
    double radians = Math.atan2(delta_y, delta_x);
    return (float) Math.toDegrees(radians);
  }
  /**
   * 两个触点的中间坐标
   *
   * @param pointM 中间坐标
   * @param event 触摸事件
   */
  private void setMidPoint(PointF pointM, MotionEvent event) {
    float x = event.getX(0) + event.getY(1);
    float y = event.getY(0) + event.getY(1);
    pointM.set(x / 2, y / 2);
  }
  /**
   * 检查约束条件(缩放倍数)
   */
  private void checkView() {
    if (currentScale > 1) {
      if (currentScale * matrixScale > maxScale) {
        matrix.postScale(maxScale / matrixScale, maxScale / matrixScale, pointM.x, pointM.y);
        matrixScale = maxScale;
      } else {
        matrix.postScale(currentScale, currentScale, pointM.x, pointM.y);
        matrixScale *= currentScale;
      }
    } else {
      if (currentScale * matrixScale else {
        matrix.postScale(currentScale, currentScale, pointM.x, pointM.y);
        matrixScale *= currentScale;
      }
    }
  }
  /**
   * 图片居中显示、判断旋转角度 小于(90 * x + 45)度图片旋转(90 * x)度 大于则旋转(90 * (x+1))
   */
  private void centerAndRotate() {
    RectF rect = new RectF(0, 0, imgWidth, imgHeight);
    matrix.mapRect(rect);
    float width = rect.width();
    float height = rect.height();
    float dx = 0;
    float dy = 0;
    if (width 2 - width / 2 - rect.left;
    } else if (rect.left > 0) {
      dx = -rect.left;
    } else if (rect.right if (height 2 - height / 2 - rect.top;
    } else if (rect.top > 0) {
      dy = -rect.top;
    } else if (rect.bottom if (rotation != 0) {
      int rotatiOnNum= (int) (rotation / 90);
      float rotatiOnAvai= new BigDecimal(rotation % 90).setScale(1, BigDecimal.ROUND_HALF_UP).floatValue();
      float realRotation = 0;
      if (rotation > 0) {
        realRotation = rotationAvai > 45 ? (rotationNum + 1) * 90 : rotationNum * 90;
      } else if (rotation 0) {
        realRotation = rotationAvai 45 ? (rotationNum - 1) * 90 : rotationNum * 90;
      }
      System.out.println("realRotation: " + realRotation);
      matrix.postRotate(realRotation, displayWidth / 2, displayHeight / 2);
      rotation = 0;
    }
  }
  /**
   * 显示网络图片时使用
   */
  private class MyTask extends AsyncTaskFile, File, Bitmap> {
    Bitmap bitmap;
    String path;
    int scale = 1;
    long size;
    @Override
    protected Bitmap doInBackground(File... params) {
      // TODO Auto-generated method stub
      try {
        size = params[0].length();
        path = params[0].getAbsolutePath();
        BitmapFactory.Options optiOns= new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        scale = calculateInSampleSize(options, displayWidth,
            displayHeight);
        options.inJustDecodeBounds = false;
        options.inSampleSize = scale;
        bitmap = BitmapFactory.decodeFile(path, options);
      } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      return bitmap;
    }
    @Override
    protected void onPostExecute(Bitmap result) {
      // TODO Auto-generated method stub
      EventBus.getDefault().post(
          new CustomEventBus(CustomEventBus.EventType.SHOW_PICTURE, result));
    }
    /**
     * 获取图片缩放比例
     *
     * @param paramOptions Options
     * @param paramInt1  宽
     * @param paramInt2  高
     * @return int
     */
    private int calculateInSampleSize(BitmapFactory.Options paramOptions,
                     int paramInt1, int paramInt2) {
      int i = paramOptions.outHeight;
      int j = paramOptions.outWidth;
      int k = 1;
      if ((i > paramInt2) || (j > paramInt1)) {
        int m = Math.round(i / paramInt2);
        int n = Math.round(j / paramInt1);
        k = m return k;
    }
  }
}
CustomEventBus.java
package com.practice.noyet.rotatezoomimageview;
/**
* package: com.practice.noyet.rotatezoomimageview
* Created by noyet on 2015/11/11.
*/
public class CustomEventBus {
  public EventType type;
  public Object obj;
  public CustomEventBus(EventType type, Object obj) {
    this.type = type;
    this.obj = obj;
  }
  enum EventType {
    SHOW_PICTURE
  }
}


推荐阅读
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 【Windows】实现微信双开或多开的方法及步骤详解
    本文介绍了在Windows系统下实现微信双开或多开的方法,通过安装微信电脑版、复制微信程序启动路径、修改文本文件为bat文件等步骤,实现同时登录两个或多个微信的效果。相比于使用虚拟机的方法,本方法更简单易行,适用于任何电脑,并且不会消耗过多系统资源。详细步骤和原理解释请参考本文内容。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
author-avatar
wojijola;
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有