Android kotlin 自定义View 旋转、移动、放缩 ImageView

该博客介绍了如何在Android中创建一个自定义的ImageView子类,实现图片的三指缩放、双指旋转及单指平移功能。通过ScaleGestureDetector监听手势,并使用Matrix矩阵变换来处理图片的缩放、旋转和移动操作。布局文件中引用了这个自定义视图,展示了其实现效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0、效果图

              

1、功能

功能:
放缩:三指操作
旋转:两只操作
移动:单指操作

 2、实现代码

import android.content.Context
import android.graphics.Matrix
import android.graphics.PointF
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import kotlin.math.atan

class RotateZoomImageView(context: Context, attrs: AttributeSet?) :
    androidx.appcompat.widget.AppCompatImageView(context, attrs) {

    private val TAG = "RotateZoomImageView"
    private var mScaleGestureDetector: ScaleGestureDetector

    private var mImageMatrix: Matrix = Matrix()
    private val savedMatrix: Matrix = Matrix()

    private var x = 0
    private var y = 0

    private var mLastAngle = 0

    // 第一个按下的手指的点
    private val startPoint = PointF()


    private val mScaleListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            //缩放比例因子
            val scaleFactor = detector.scaleFactor
            mImageMatrix.postScale(scaleFactor, scaleFactor, x.toFloat(), y.toFloat())
            imageMatrix = mImageMatrix

            return true
        }
    }

    init {
        mScaleGestureDetector = ScaleGestureDetector(context, mScaleListener)
        scaleType = ScaleType.MATRIX
    }


    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        if (w != oldw || h != oldh) {
            val transX = (w - drawable.intrinsicWidth) / 2.0
            val transY = (h - drawable.intrinsicHeight) / 2.0
            mImageMatrix.setTranslate(transX.toFloat(), transY.toFloat())
            imageMatrix = mImageMatrix
            x = w / 2
            y = h / 2
        }
    }


    private fun doRotationEvent(ev: MotionEvent): Boolean {
        //计算两个手指的角度
        val dx = ev.getX(0) - ev.getX(1)
        val dy = ev.getY(0) - ev.getY(1)
        //弧度
        val radians = atan(dy.toDouble() / dx.toDouble())
        //角度
        val degrees = (radians * 180 / Math.PI).toInt()

        when (ev.actionMasked) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_POINTER_UP -> mLastAngle =
                degrees
            MotionEvent.ACTION_MOVE -> {
                when {
                    (degrees - mLastAngle) > 45 -> mImageMatrix.postRotate(
                        -5f,
                        x.toFloat(),
                        y.toFloat()
                    )
                    (degrees - mLastAngle) < -45 -> mImageMatrix.postRotate(
                        5f,
                        x.toFloat(),
                        y.toFloat()
                    )
                    else -> mImageMatrix.postRotate(
                        (degrees - mLastAngle).toFloat(),
                        x.toFloat(),
                        y.toFloat()
                    )
                }
                imageMatrix = mImageMatrix
                mLastAngle = degrees
            }
        }

        return true
    }

    private fun doMoveEvent(ev: MotionEvent): Boolean {
        when (ev.actionMasked) {
            MotionEvent.ACTION_MOVE -> {
                mImageMatrix.set(savedMatrix)
                mImageMatrix.postTranslate(ev.x - startPoint.x, ev.y - startPoint.y)
                imageMatrix = mImageMatrix
            }
        }
        return true
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_DOWN) {
            Log.d("drag", "onTouch: x= ${event.rawX.toInt()},y=${event.rawY.toInt()}" )
            mImageMatrix.set(imageMatrix)
            savedMatrix.set(mImageMatrix)
            startPoint[event.x] = event.y
            return true
        }
        return when (event.pointerCount) {
            3 -> mScaleGestureDetector.onTouchEvent(event)
            2 -> doRotationEvent(event)
            1 -> doMoveEvent(event)
            else -> super.onTouchEvent(event)
        }
    }
}

3、布局引用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".RotateImageActivity">


    <com.afei.cropphoto.RotateZoomImageView
        android:id="@+id/rotate_img"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginHorizontal="30dp"
        android:layout_marginVertical="50dp"
        android:background="#DEDDDD"
        android:src="@drawable/mv"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yongfeicao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值