首页 > 其他分享 >Android 自定义图片拖动、缩放、旋转

Android 自定义图片拖动、缩放、旋转

时间:2024-08-02 16:43:07浏览次数:18  
标签:自定义 缩放 float private event context Android public

Android 图片拖动、缩放、旋转

图片拖动

定义一个类,继承AppCompatImageView

public class MyImageView extends AppCompatImageView {
    //实现方法
    public MyImageView(Context context) {
        this(context, null);
    }
    //实现方法
    public MyImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    //实现方法
    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }
}

在资源管理器中添加图片

在布局中使用自定义控件

  <com.jing.ScaleImageView.view.MyImageView
      android:src="@drawable/fox"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />
<!-- android:src="@drawable/fox" 为自己图片资源,存放在res/drawable目录下,fox为无后缀的文件名 -->

实现图片拖动

public class MyImageView extends AppCompatImageView {
    
    private Matrix matrix = new Matrix();
    //用于对图像进行变换(在这个例子中是平移)。
    private float lastX, lastY;
    //存储上一次触摸事件的位置。
    public MyImageView(Context context) {
        this(context, null);
    }
    public MyImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }
    
    
    private void init(Context context) {
        setScaleType(ScaleType.MATRIX);
        //将通过 Matrix 进行控制图像的缩放和变换
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //确保图像被正确绘制。
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action = event.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                //ACTION_DOWN: 记录触摸开始的位置。
                lastX = event.getX();
                lastY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                //ACTION_MOVE: 计算触摸移动的距离,
                float dx = event.getX() - lastX;
                float dy = event.getY() - lastY;
                matrix.postTranslate(dx, dy);
                setImageMatrix(matrix);
                //平移图像。然后更新视图的
        }
        return true;
    }
}

图片缩放

在图片平移的基础上添加,具体代码如下

public class MyImageView extends AppCompatImageView {
	
    private Matrix matrix = new Matrix();
    //用于对图像进行变换(在这个例子中是平移/缩放)。
    private ScaleGestureDetector scaleDetector;
    //用于检测和处理缩放手势。
    private float lastX, lastY;
    //存储上一次触摸事件的位置。
    private boolean allPointersUp;
    // 用于跟踪是否所有触摸点都已抬起,以区分平移和缩放操作。

    public MyImageView(Context context) {
        this(context, null);
    }

    public MyImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        //初始化了 ScaleGestureDetector 以处理缩放手势
        setScaleType(ScaleType.MATRIX);
        //将 ScaleType 设置为 MATRIX。将通过 Matrix 进行控制图像的缩放和变换。
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //进入缩放方法
        scaleDetector.onTouchEvent(event);
        final int action = event.getActionMasked();
        switch (action) {
            //记录触摸开始的位置,并将 allPointersUp 设置为 false。
            case MotionEvent.ACTION_DOWN:
                lastX = event.getX();
                lastY = event.getY();
                allPointersUp = false;
                break;
            //记录第二个触摸点按下的事件,将 allPointersUp 设置为 true。
            case MotionEvent.ACTION_POINTER_DOWN:
                allPointersUp = true;
                break;
            case MotionEvent.ACTION_MOVE:
                //如果是缩放就禁止移动
                if (!allPointersUp) {
                    float dx = event.getX() - lastX;
                    float dy = event.getY() - lastY;
                        matrix.postTranslate(dx, dy);
                        setImageMatrix(matrix);
                        lastX = event.getX();
                        lastY = event.getY();
                }
                break;
        }
        return true;
    }

	//处理缩放事件
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            //提供了缩放手势的缩放因子,允许你根据用户的缩放手势调整图像的显示大小
            float scaleFactorChange = detector.getScaleFactor();
            // 获取ImageView的中心点作为缩放中心点
            float px = getWidth() / 2.0f;
            float py = getHeight() / 2.0f;
            // 在ImageView的中心点进行缩放
            matrix.postScale(scaleFactorChange, scaleFactorChange, px, py);
            // 应用新的矩阵变换
            setImageMatrix(matrix);
            return true;
        }
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            //缩放手势结束时调用,可以在此方法中执行任何必要的清理操作。
            super.onScaleEnd(detector);
        }
    }
}

图片旋转

public class MyImageView extends AppCompatImageView {
    private Matrix matrix = new Matrix();
    private ScaleGestureDetector scaleDetector;
    private float lastX, lastY;
    private boolean isDragging;
    //存储初始旋转角度
    private float d;
    //存储当前角度
    private float newRot;

    public MyImageView(Context context) {
        this(context, null);
    }

    public MyImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        setScaleType(ScaleType.MATRIX);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        scaleDetector.onTouchEvent(event);
        //进入旋转方法
        handleRotation(event);

        final int action = event.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                lastX = event.getX();
                lastY = event.getY();
                isDragging = true;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                isDragging = false;
                break;
            case MotionEvent.ACTION_MOVE:
                if (isDragging) {
                    float dx = event.getX() - lastX;
                    float dy = event.getY() - lastY;
                    matrix.postTranslate(dx, dy);
                    setImageMatrix(matrix);
                    lastX = event.getX();
                    lastY = event.getY();
                }
                break;
        }
        return true;
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactorChange = detector.getScaleFactor();
            float px = getWidth() / 2.0f;
            float py = getHeight() / 2.0f;
            matrix.postScale(scaleFactorChange, scaleFactorChange, px, py);
            setImageMatrix(matrix);
            return true;
        }
    }
    //处理旋转事件
    private void handleRotation(MotionEvent event) {
        //只有是俩个手指按下才会继续下去
        if (event.getPointerCount() == 2) {
            if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
                //计算并保存初始的旋转角度
                d = rotation(event);
            } else if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
                newRot = rotation(event);
                //计算当前的旋转角度。
                matrix.postRotate(newRot - d, getWidth() / 2.0f, getHeight() / 2.0f);
                //计算从上一次旋转角度 d 到当前旋转角度 newRot 的增量,并将这个增量应用到 Matrix 上。
                setImageMatrix(matrix);
                //更新视图的 Matrix,以应用旋转变换。
                d = newRot;
                //更新 d 为当前的旋转角度,以便在下次移动事件中计算正确的旋转增量。
            }
        }
    }
	//旋转角度计算
    private float rotation(MotionEvent event) {
        double delta_x = event.getX(1) - event.getX(0);
        double delta_y = event.getY(1) - event.getY(0);
        return (float) Math.toDegrees(Math.atan2(delta_y, delta_x));
    }
}

图片拖动、缩放和旋转实现总结

1. 图片拖动

  • 创建自定义的 ImageView 类,继承 AppCompatImageView
  • 使用 Matrix 对图像进行平移变换。
  • 处理 MotionEvent 中的触摸事件,计算拖动的距离,并更新图像的位置。

2. 图片缩放

  • 在自定义 ImageView 中,添加对 ScaleGestureDetector 的支持。
  • ScaleGestureDetector 处理缩放手势,并提供缩放因子。
  • 使用 Matrix 进行缩放变换,更新图像显示。

3. 图片旋转

  • 使用两个触摸点来计算旋转角度的变化。
  • 应用旋转变换到 Matrix,更新图像显示。

标签:自定义,缩放,float,private,event,context,Android,public
From: https://www.cnblogs.com/20lxj666/p/18339053

相关文章

  • Android开发 - (适配器)Adapter类中ArrayObjectAdapter实现类详细解析
    简介用于AndroidTV的Leanback库,用于绑定对象数组到UI组件具体作用ArrayObjectAdapter是RecyclerView和Adapter系列中用于处理列表数据的一种适配器类型,主要用于AndroidTV的Leanback库中的BrowseFragment、DetailFragment和PlaybackOverlayFragment等......
  • 【C#】WPF实现HaIcon图像缩放、移动
    1.HaIcon实现的C#代码////FilegeneratedbyHDevelopforHALCON/DOTNET(C#)Version12.0////ThisfileisintendedtobeusedwiththeHDevelopTemplateor//HDevelopTemplateWPFprojectslocatedunder%HALCONEXAMPLES%\c#usingSystem;usingSystem.Thre......
  • HarmonyOS:如何实现自定义的Tabs,TabContent内部实现如何动态配置
    前言:最近做开发任务的时候,想把Tabs自定义了,并且动态配置TabContent里面的内容,不是写死一样的,这个问题困扰了很长时间,试过**@BuilderParam**(类似于vue的插槽)传组件方式的,但是**@BuilderParam只能传一个,我想要传递的是一个数组,找了很多Api最后找到了WrappedBuilder[]**这种方......
  • Java计算机毕业设计基于Android的自闭症康复训练APP设计与实现(开题报告+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景自闭症,作为一种复杂的神经发育障碍,影响着全球数百万儿童及其家庭。自闭症患者在社交互动、沟通表达及行为模式上常表现出显著困难。随着移动技术的飞......
  • 学习Android-2024-08
    学习Android-2024-08-01今天内容没有具体在程序中验证,可能存在问题。明天验证。1.打印日志1.1共5个级别,Log.e、Log.w、Log.i、Log.d、Log.v,重要性依次降低。例如Log.v会看到前面Log.e、Log.w等所有的信息。而Log.e只会看到Log.e的信息。1.2输出时打的tag,利于在控制台进行搜索......
  • Androidstudio开发,购物商城app 实现商品详情页(六)
    文章目录1.涉及到的技术点2.代码实现过程3.运行效果图4.参考学习文章相关视频教程在某站上面(......
  • 自定义的 systemd 服务启动方式
    目录systemd单元文件(UnitFile)单元文件结构示例单元文件1.基础单元文件2.带有环境变量的单元文件3.自定义的ExecStartPre和ExecStartPost配置管理日志管理1.系统日志:2.应用程序日志:3.用户日志:使用prometheus配置实例1.配置prometheus2.配置alertmana......
  • UE5-自定义插件使用第三方库
    制作插件使用到了第三方库,后面很长时间没有用这个插件,导致插件启用不了,吃亏了,所以记录下制作过程。第一步:在继承ModuleRules的C#脚本里添加代码:privatestringModulePath { get { returnModuleDirectory; } } privatestringThirdPartyPath { get{re......
  • WPF 自定义对话框
    <Windowx:Class="WPFDemo2.窗体.CustomDialogWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas......
  • Photos框架 - 自定义媒体选择器(相册列表)
    ​​​​​​​Photos框架-自定义媒体资源选择器(数据部分)Photos框架-自定义媒体选择器(UI列表)​​​​​​​Photos框架-自定义媒体选择器(UI预览)Photos框架-自定义媒体选择器(相册列表)引言我们已经实现了媒体资源的列表选择以及媒体资源的大图预览功能,但通常一个......