"我正在参与「启航计划」"
这是我的第二篇文章
我发现大家对于实用性强的文章更感兴趣,这儿就写一下之前写的一个自定义一个支撑手势缩放以及点击旋转的控件
无图无本相,先上效果图
一、主要实现流程如下:
-
说到Android中的缩放,那肯定与scaleX,scaleY有关,经过这两个api能够方便的实现view的扩大缩小 可是这样的话会有问题咱们能够幻想一下假定手机是1080×1920,控件巨细也是1080×1920, 这时刚好填充满手机屏幕,可是当扩大一倍后
就会出现问题,什么问题呢?假定原先控件上四个边角都有一个4×4的带色彩的小方块,当控件扩大两倍后为2160×3840,这时手机屏幕中内容显现不全 鉴于这种问题咱们一般是让该控件可被用户拖拽检查,拖拽的话就要使用到translationX,与translationY -
既然是平移那就触及到了x与y方向能够平移多远间隔的问题,有一个平移鸿沟,这个鸿沟便是咱们缩放增加的巨细,
假如说缩放到2160×3840,x方向增大了1080,那么能够向左向右各平移最多540的间隔,具体的移动能够在 onTouchEvent方法中进行核算设置,
另外在进行缩放操作时假如发现控件的x,y进行过平移那么将translationX,与translationY置为0进行一个复位 -
到了这儿缩放与平移现已完结,后面便是经过手势双指进行一个缩放操作,经过 event的MotionEvent.ACTION_POINTER_DOWN,检测双指事情
然后经过三角函数得到两点之间的间隔spacing1,缩放时分移动得到两指间隔spacing2,得到缩放系数 spacing = spacing2/spacing1 然后乘以现有的缩放份额:mScaleFactor = mScaleFactor * spacing,得到控件将要缩放的份额,在缩放过程中也要进行缩放鸿沟的判别 -
现在要害便是旋转问题了,由于旋转后控件宽高比较于屏幕宽高改变,一些缩放系数与鸿沟判别能够看doSetCWRotation方法,另外移动的时分也是要根据旋转视点进行判别的
二、代码如下:
细节当地代码中有相关注释
xml文件:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context=".MainActivity">
<com.example.mytestdemo.scale.zoom.HomeworkZoomView
android:id="@+id/scale_view"
android:layout_width="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="#E11F1F"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="100dp"
android:background="@color/black"
android:layout_height="100dp">
<TextView
android:layout_width="wrap_content"
android:text="left"
android:layout_gravity="center"
android:textColor="@color/purple_700"
android:textSize="16sp"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_gravity="center"
android:orientation="vertical"
android:gravity="center"
android:layout_height="wrap_content">
<Button
android:id="@+id/tv_rotation"
android:layout_width="wrap_content"
android:text="旋转"
android:textColor="@color/purple_700"
android:textSize="16sp"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/tv_add_scale"
android:layout_width="wrap_content"
android:text="扩大"
android:textColor="@color/purple_700"
android:textSize="16sp"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/tv_reduce_scale"
android:layout_width="wrap_content"
android:text="减小"
android:textColor="@color/purple_700"
android:textSize="16sp"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:background="@color/black"
android:layout_gravity="right"
android:layout_height="100dp">
<TextView
android:layout_width="wrap_content"
android:text="right"
android:layout_gravity="center"
android:textColor="@color/purple_700"
android:textSize="16sp"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:background="@color/black"
android:layout_gravity="bottom|left"
android:layout_height="100dp">
<TextView
android:layout_width="wrap_content"
android:text="bottom|left"
android:layout_gravity="center"
android:textColor="@color/purple_700"
android:textSize="16sp"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:background="@color/black"
android:layout_gravity="bottom|right"
android:layout_height="100dp">
<TextView
android:layout_width="wrap_content"
android:text="bottom right"
android:layout_gravity="center"
android:textColor="@color/purple_700"
android:textSize="16sp"
android:layout_height="wrap_content"/>
</LinearLayout>
</com.example.mytestdemo.scale.zoom.HomeworkZoomView>
</LinearLayout>
控件源码
package com.example.mytestdemo.scale.zoom
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout
import androidx.core.view.GestureDetectorCompat
/**
* @Description:支撑控件缩放,旋转, 双击扩大缩小,制作, 生成图片等
* @Author: tgw
* @CreateDate: 2022/4/22 10:46
*/
class HomeworkZoomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
var TAG = "HomeworkZoomView"
// 属性变量
private var translationX // 移动X
= 0f
private var translationY // 移动Y
= 0f
private var mScaleFactor = 1f // 伸缩份额
private var mRotation // 旋转视点
= 0
// 移动过程中暂时变量
private var actionX = 0f
private var actionY = 0f
private var spacing = 0f
private var moveType // 0=未挑选,1=correct_tool_drag,2=缩放
= 0
//动画正在履行中
private var isDoing = false
private val MAX_SCALE = 2.0f
private val MIN_SCALE = 1.0f
var mGestureDetector: GestureDetectorCompat? = null
private var mLastTouchX = 0f
private var mLastTouchY = 0f
private var originWidth //控件的原始长度
= 0
private var originHeight //控件的原始宽度
= 0
private var originScale = 1f //缩放的回弹参数,作为缩放份额最小系数
private val isOpenMultiFingered = false
private fun init(context: Context) {
mGestureDetector = GestureDetectorCompat(getContext(), CorrectGestureListener())
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//获取控件的长宽
originWidth = MeasureSpec.getSize(widthMeasureSpec)
originHeight = MeasureSpec.getSize(heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
//getParent().requestDisallowInterceptTouchEvent(true);
// if(ev.getAction() == MotionEvent.ACTION_MOVE){
// if(mScaleFactor == 1){
// return false;
// }
// }
return super.onInterceptTouchEvent(ev)
}
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
return super.dispatchTouchEvent(ev)
}
interface OnSetTouchEventListener {
fun setTouchEvent(event: MotionEvent?)
fun cancel()
}
private var setTouchEventListener: OnSetTouchEventListener? = null
fun setTouchEvent(setTouchEventListener: OnSetTouchEventListener?) {
this.setTouchEventListener = setTouchEventListener
}
var notDo = false
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
// Log.e("seesee", "onTouchEvent " + event.getActionMasked());
if (isDoing) {
return super.onTouchEvent(event)
}
if (isOpenMultiFingered) {
if (event.pointerCount == 1) {
if (setTouchEventListener != null) {
setTouchEventListener!!.setTouchEvent(event)
parent.requestDisallowInterceptTouchEvent(true)
return super.onTouchEvent(event)
}
}
if (event.actionMasked == MotionEvent.ACTION_POINTER_DOWN) {
if (setTouchEventListener != null) {
setTouchEventListener!!.cancel()
}
}
}
//注册双击事情
if (mGestureDetector != null) {
val a = mGestureDetector!!.onTouchEvent(event)
}
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
moveType = 1
actionX = event.rawX
actionY = event.rawY
}
MotionEvent.ACTION_POINTER_DOWN -> {
moveType = 2
spacing = getSpacing(event)
mLastTouchX = event.rawX
mLastTouchY = event.rawY
Log.d(TAG, "onTouchEvent ACTION_POINTER_DOWN mLastTouchX: $mLastTouchX")
Log.d(TAG, "onTouchEvent ACTION_POINTER_DOWN mLastTouchY: $mLastTouchY")
}
MotionEvent.ACTION_MOVE -> if (isOpenMultiFingered) {
Log.d(TAG, "onTouchEvent isOpenMultiFingered mScaleFactor: $mScaleFactor")
Log.d(TAG, "onTouchEvent isOpenMultiFingered spacing: $spacing")
mScaleFactor = mScaleFactor * getSpacing(event) / spacing
Log.d(TAG, "onTouchEvent isOpenMultiFingered mScaleFactor: $mScaleFactor")
val canScale = checkScaleBound()
if (canScale) {
//缩放过程中方位偏移
val disX = event.rawX - mLastTouchX
val disY = event.rawY - mLastTouchY
Log.d(TAG, "onTouchEvent isOpenMultiFingered translationX: $translationX")
Log.d(TAG, "onTouchEvent isOpenMultiFingered translationY: $translationY")
setTranslationXY(translationX + disX, translationY + disY)
}
mLastTouchX = event.rawX
mLastTouchY = event.rawY
} else {
if (moveType == 1) {
var canMove = false
canMove = if (mRotation == 90 || mRotation == 270) {
checkRotationTranslation(event.rawX - actionX, event.rawY - actionY)
} else {
checkTranslation(event.rawX - actionX, event.rawY - actionY)
}
actionX = event.rawX
actionY = event.rawY
if (!canMove) {
zoomNotHandler = true
parent.requestDisallowInterceptTouchEvent(false)
} else {
zoomNotHandler = false
}
} else if (moveType == 2) {
Log.d(TAG, "onTouchEvent moveType == 2 mScaleFactor: $mScaleFactor")
Log.d(TAG, "onTouchEvent moveType == 2 spacing: $spacing")
mScaleFactor = mScaleFactor * getSpacing(event) / spacing
Log.d(TAG, "onTouchEvent moveType == 2 mScaleFactor: $mScaleFactor")
val canScale = checkScaleBound()
if (canScale) {
//缩放过程中方位偏移
val disX = event.rawX - mLastTouchX
val disY = event.rawY - mLastTouchY
Log.d(TAG, "onTouchEvent moveType == 2 mLastTouchX: $mLastTouchX")
Log.d(TAG, "onTouchEvent moveType == 2 mLastTouchY: $mLastTouchY")
Log.d(TAG, "onTouchEvent moveType == 2 disX: $disX")
Log.d(TAG, "onTouchEvent moveType == 2 disY: $disY")
Log.d(TAG, "onTouchEvent moveType == 2 event.getRawX(): " + event.rawX)
Log.d(TAG, "onTouchEvent moveType == 2 event.getRawY(): " + event.rawY)
Log.d(TAG, "onTouchEvent moveType == 2 translationX: $translationX")
Log.d(TAG, "onTouchEvent moveType == 2 translationY: $translationY")
setTranslationXY(translationX + disX, translationY + disY)
}
mLastTouchX = event.rawX
mLastTouchY = event.rawY
Log.d(TAG, "onTouchEvent moveType == 2 endm LastTouchX: $mLastTouchX")
Log.d(TAG, "onTouchEvent moveType == 2 endm mLastTouchY: $mLastTouchY")
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
upAnimator(moveType)
moveType = 0
}
}
return super.onTouchEvent(event)
}
private fun checkTranslation(disX: Float, disY: Float): Boolean {
//判别左右滑,仍是上下滑
//假如制作区域在可视范围内,则不能滑动x,y坐标
val width = originWidth
val height = originHeight
val tempWidth = width * mScaleFactor
val tempHeight = height * mScaleFactor
// if(mRotation == 90 || mRotation == 270){
// tempWidth = height*mScaleFactor;
// tempHeight = width*mScaleFactor;
// }
Log.d(TAG, "checkTranslation width: $width")
Log.d(TAG, "checkTranslation height: $height")
Log.d(TAG, "checkTranslation mScaleFactor: $mScaleFactor")
Log.d(TAG, "checkTranslation tempWidth: $tempWidth")
Log.d(TAG, "checkTranslation tempHeight: $tempHeight")
val canMove = true
if (tempWidth <= width && tempHeight <= height) {
return false
}
var resultX = translationX + disX
var resultY = translationY + disY
Log.d(TAG, "checkTranslation disX: $disX")
Log.d(TAG, "checkTranslation disY: $disY")
Log.d(TAG, "checkTranslation translationX: $translationX")
Log.d(TAG, "checkTranslation translationY: $translationY")
Log.d(TAG, "checkTranslation resultX: $resultX")
Log.d(TAG, "checkTranslation resultY: $resultY")
if (tempWidth <= width) {
resultX = translationX //不动
} else {
val maxWDis = (tempWidth - width) / 2
if (resultX <= -maxWDis) {
resultX = -maxWDis
// canMove = false;
} else if (resultX >= maxWDis) {
resultX = maxWDis
// canMove = false;
} else {
parent.requestDisallowInterceptTouchEvent(true)
}
}
if (tempHeight <= height) {
resultY = translationY
} else {
val maxHDis = (tempHeight - height) / 2
if (resultY <= -maxHDis) {
resultY = -maxHDis
// canMove = false;
} else if (resultY >= maxHDis) {
resultY = maxHDis
// canMove = false;
} else {
parent.requestDisallowInterceptTouchEvent(true)
}
}
if (canMove) {
//doUp = false;
setTranslationXY(resultX, resultY)
}
return canMove
}
private fun checkRotationTranslation(disX: Float, disY: Float): Boolean {
//判别左右滑,仍是上下滑
//假如制作区域在可视范围内,则不能滑动x,y坐标
val width = originWidth
val height = originHeight
val tempWidth = height * mScaleFactor
val tempHeight = width * mScaleFactor
Log.d(TAG, "checkTranslation width: $width")
Log.d(TAG, "checkTranslation height: $height")
Log.d(TAG, "checkTranslation mScaleFactor: $mScaleFactor")
Log.d(TAG, "checkTranslation tempWidth: $tempWidth")
Log.d(TAG, "checkTranslation tempHeight: $tempHeight")
var canMove = true
if (tempWidth <= width && tempHeight <= height) {
return false
}
var resultX = translationX + disX
var resultY = translationY + disY
Log.d(TAG, "checkTranslation disX: $disX")
Log.d(TAG, "checkTranslation disY: $disY")
Log.d(TAG, "checkTranslation translationX: $translationX")
Log.d(TAG, "checkTranslation translationY: $translationY")
Log.d(TAG, "checkTranslation resultX: $resultX")
Log.d(TAG, "checkTranslation resultY: $resultY")
if (tempWidth <= width) {
resultX = translationX //不动
} else {
val maxWDis = (tempWidth - width) / 2
if (resultX <= -maxWDis) {
resultX = -maxWDis
canMove = false
} else if (resultX >= maxWDis) {
resultX = maxWDis
canMove = false
} else {
parent.requestDisallowInterceptTouchEvent(true)
}
}
if (tempHeight <= height) {
resultY = translationY
} else {
val maxHDis = (tempHeight - height) / 2
if (resultY <= -maxHDis) {
resultY = -maxHDis
canMove = false
} else if (resultY >= maxHDis) {
resultY = maxHDis
canMove = false
} else {
parent.requestDisallowInterceptTouchEvent(true)
}
}
if (canMove) {
//doUp = false;
setTranslationXY(resultX, resultY)
}
return canMove
}
private fun upAnimator(moveType: Int) {
if (isDoing || moveType != 2) return //双击事情缩放中回来
val width = originWidth
val height = originHeight
var tempWidth = width * mScaleFactor
var tempHeight = height * mScaleFactor
if (mRotation == 90 || mRotation == 270) {
tempWidth = height * mScaleFactor
tempHeight = width * mScaleFactor
}
//没有改变则不需求判别
if (translationX == 0f && translationY == 0f && mScaleFactor == originScale) {
return
}
val resultX: Float
val resultY: Float
if (mRotation == 90 || mRotation == 270) {
resultX = (1 - mScaleFactor) * originHeight + Math.abs(translationX)
resultY = (1 - mScaleFactor) * originWidth + Math.abs(translationY)
Log.d(TAG, "upAnimator mScaleFactor: $mScaleFactor")
Log.d(TAG, "upAnimator tempHeight: $tempHeight")
Log.d(TAG, "upAnimator tempWidth: $tempWidth")
Log.d(TAG, "upAnimator width: $width")
Log.d(TAG, "upAnimator height: $height")
Log.d(
TAG,
"upAnimator (1 - mScaleFactor) * originHeight: " + (1 - mScaleFactor) * originHeight
)
Log.d(
TAG,
"upAnimator (1 - mScaleFactor) * originWidth: " + (1 - mScaleFactor) * originWidth
)
Log.d(
TAG,
"upAnimator ((1 - mScaleFactor) * originHeight - width) / 2: " + ((1 - mScaleFactor) * originHeight - width) / 2
)
Log.d(
TAG,
"upAnimator ((1 - mScaleFactor) * originWidth - height) / 2: " + ((1 - mScaleFactor) * originWidth - height) / 2
)
Log.d(TAG, "upAnimator translationX: $translationX")
Log.d(TAG, "upAnimator translationY: $translationY")
Log.d(TAG, "upAnimator resultX: $resultX")
Log.d(TAG, "upAnimator resultY: $resultY")
val isNeedBack = isRotationNeedBack(
mScaleFactor,
resultX,
resultY,
originWidth.toFloat(),
originHeight.toFloat()
)
//根据调查,向下滑translationY大于0,向上滑translationY小于0
//在缩放系数小于1的时分,resultY永久大于0,当缩小时,缩小的间隔加上 移动间隔的绝对值小于当时的布局的一半时
if (isNeedBack) {
zoomAnimator(mScaleFactor, originScale, 0f, 0f)
return
}
} else {
resultX = (1 - mScaleFactor) * originWidth + Math.abs(translationX)
resultY = (1 - mScaleFactor) * originHeight + Math.abs(translationY)
val isNeedBack = isRotationNeedBack(
mScaleFactor,
resultX,
resultY,
originHeight.toFloat(),
originWidth.toFloat()
)
if (isNeedBack) {
zoomAnimator(mScaleFactor, originScale, 0f, 0f)
return
}
}
}
private fun isNeedBack(
resultX: Float,
resultY: Float,
tempWidth: Float,
tempHeight: Float
): Boolean {
if (resultX > 0) {
return true
} else if (resultX + tempWidth < originWidth) {
return true
}
if (resultY > 0) {
return true
} else if (resultY + tempHeight < originHeight) {
return true
}
return false
}
/**
* 判别扩大的时分旋转,判别是否需求还原巨细
*/
private fun isRotationNeedBack(
scaleFactor: Float,
resultX: Float,
resultY: Float,
originHeight: Float,
originWidth: Float
): Boolean {
//根据调查,向下滑translationY大于0,向上滑translationY小于0
//在缩放系数小于1的时分,resultY永久大于0,当缩小时,缩小的间隔加上 移动间隔的绝对值,小于当时的布局的一半时,
// 以为你应该要恢复原状了,避免缩小时 偏移到到看不见了
//在缩放系数大于1的时分,((1 - mScaleFactor) * originWidth)的值,假定mScaleFactor为最大的2倍时,
// 当未移动时resultY=-originWidth,当移动后就会translationX就会发生偏移量(上面获得绝对值:即偏移量)
//当缩小时,((1 - mScaleFactor) * originWidth)的值会变小,当缩放的巨细+偏移量>0时
// 以为整体应该要恢复原状了,避免缩小时 偏移到到看不见了
var isNeedBack = false
if (scaleFactor < 1 && resultY > originHeight / 2) { //当90时原始宽高颠倒
isNeedBack = true
} else if (scaleFactor > 1 && resultY > 0) {
//在缩放系数>于1的时分,resultY永久大于0,当缩小时,
// 缩小的间隔加上 移动间隔的绝对值小于当时的布局的一半时
isNeedBack = true
}
if (scaleFactor < 1 && resultX > originWidth / 2) {
isNeedBack = true
} else if (scaleFactor > 1 && resultX > 0) {
isNeedBack = true
}
return isNeedBack
}
/**
* 移动拖拽view
*/
private fun setTranslationXY(transX: Float, transY: Float) {
translationX = transX
translationY = transY
Log.d(TAG, "setTranslationXY translationX: $translationX")
Log.d(TAG, "setTranslationXY translationY: $translationY")
//Log.e("xxyy", "translationX="+translationX+" || translationY="+translationY);
setTranslationX(translationX)
setTranslationY(translationY)
}
/**
* 缩放鸿沟值判别
*
* @return 判别达到鸿沟值中止后续操作
*/
private fun checkScaleBound(): Boolean {
val canScale: Boolean
Log.d(TAG, "checkScaleBound--mScaleFactor: $mScaleFactor")
Log.d(TAG, "checkScaleBound--mScaleFactor--: " + mScaleFactor / originScale)
if (getTranslationX() != 0f || getTranslationY() != 0f) {
setTranslationXY(0f, 0f)
}
if (mScaleFactor < originScale) {
canScale = false
mScaleFactor = originScale
} else if (mScaleFactor > 2 * originScale) {
canScale = false
mScaleFactor = 2 * originScale
} else {
canScale = true
parent.requestDisallowInterceptTouchEvent(true)
}
scaleX = mScaleFactor
scaleY = mScaleFactor
return canScale
}
/**
* 设置确认视点
*/
fun doSetRotation(rotation: Int) {
mRotation = rotation
var tempScale = 1f
setTranslationX(0.also { translationX = it.toFloat() }.toFloat())
setTranslationY(0.also { translationY = it.toFloat() }.toFloat())
if (mRotation == 0) {
originScale = 1f
tempScale = 1f
} else if (mRotation == 90) {
originScale = this.height.toFloat() / this.width
tempScale = originScale
} else if (mRotation == 180) {
originScale = 1f
tempScale = 1f
} else if (mRotation == 270) {
originScale = this.height.toFloat() / this.width
tempScale = originScale
}
mScaleFactor = mScaleFactor * tempScale
checkScaleBound()
this.rotation = mRotation.toFloat()
}
/**
* 对外方法,动态旋转,规则四个视点,顺时针
*/
fun doSetCWRotation() {
var tempScale = 1f
setTranslationX(0.also { translationX = it.toFloat() }.toFloat())
setTranslationY(0.also { translationY = it.toFloat() }.toFloat())
if (mRotation == 0) {
originScale = this.height.toFloat() / this.width
tempScale = originScale
mRotation = 90
} else if (mRotation == 90) {
originScale = 1f
tempScale = this.width.toFloat() / this.height
mRotation = 180
} else if (mRotation == 180) {
originScale = this.height.toFloat() / this.width
tempScale = originScale
mRotation = 270
} else if (mRotation == 270) {
originScale = 1f
tempScale = this.width.toFloat() / this.height
mRotation = 0
}
// Log.d(TAG, "doSetRotation mRotation: "+mRotation);
// Log.d(TAG, "doSetRotation originScale: "+originScale);
// Log.d(TAG, "doSetRotation tempScale: "+tempScale);
// Log.d(TAG, "doSetRotation translationX: "+translationX);
// Log.d(TAG, "doSetRotation translationY: "+translationY);
// Log.d(TAG, "doSetRotation getWidth(): "+getWidth());
// Log.d(TAG, "doSetRotation getHeight(): "+getHeight());
mScaleFactor = mScaleFactor * tempScale
checkScaleBound()
this.rotation = mRotation.toFloat()
if (setRotationListener != null) {
setRotationListener!!.setRotation(position, mRotation)
}
}
/**
* 按照份额系数进行xy的缩放
*/
fun doSetScaleXY(multiple: Float) {
mScaleFactor = originScale * multiple
checkScaleBound()
}
/**
* 对外方法,动态旋转,规则四个视点,逆时针
*/
fun doSetCCWRotation() {
var tempScale = 1f
setTranslationX(0.also { translationX = it.toFloat() }.toFloat())
setTranslationY(0.also { translationY = it.toFloat() }.toFloat())
if (mRotation == 0) {
originScale = this.height.toFloat() / this.width
tempScale = originScale
mRotation = 270
} else if (mRotation == 90) {
originScale = 1f
tempScale = this.width.toFloat() / this.height
mRotation = 0
} else if (mRotation == 180) {
originScale = this.height.toFloat() / this.width
tempScale = originScale
mRotation = 90
} else if (mRotation == 270) {
originScale = 1f
tempScale = this.width.toFloat() / this.height
mRotation = 180
}
// Log.d(TAG, "doSetRotation mRotation: "+mRotation);
// Log.d(TAG, "doSetRotation originScale: "+originScale);
// Log.d(TAG, "doSetRotation tempScale: "+tempScale);
// Log.d(TAG, "doSetRotation translationX: "+translationX);
// Log.d(TAG, "doSetRotation translationY: "+translationY);
// Log.d(TAG, "doSetRotation getWidth(): "+getWidth());
// Log.d(TAG, "doSetRotation getHeight(): "+getHeight());
mScaleFactor = mScaleFactor * tempScale
checkScaleBound()
this.rotation = mRotation.toFloat()
if (setRotationListener != null) {
setRotationListener!!.setRotation(position, mRotation)
}
}
// 触碰两点间间隔
private fun getSpacing(event: MotionEvent): Float {
//经过三角函数得到两点间的间隔
val x = event.getX(0) - event.getX(1)
val y = event.getY(0) - event.getY(1)
return Math.sqrt((x * x + y * y).toDouble()).toFloat()
}
val bitmap: Bitmap?
get() {
val width = this.width
val height = this.height
if (height <= 0 || width <= 0) {
return null
}
val mScreenshot = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444)
val c = Canvas(mScreenshot)
c.drawFilter = PaintFlagsDrawFilter(
0,
Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG
)
draw(c)
return mScreenshot
}
fun getmRotation(): Int {
return mRotation
}
fun setAddScale(add: Float) {
// BigDecimal b1 = new BigDecimal(Float.toString(mScaleFactor));
// BigDecimal b2 = new BigDecimal(Float.toString(add));
// float addScale = b1.add(b2).floatValue();
//
//
// if (addScale > MAX_SCALE) {
// return;
// }
mScaleFactor = mScaleFactor + add * originScale
checkScaleBound()
upAnimator(2)
}
fun setReduceScale(reduce: Float) {
// BigDecimal b1 = new BigDecimal(Float.toString(mScaleFactor));
// BigDecimal b2 = new BigDecimal(Float.toString(reduce));
// float reduceScale = b1.subtract(b2).floatValue();
// if (reduceScale < MIN_SCALE) {
// return;
// }
mScaleFactor = mScaleFactor - reduce * originScale
checkScaleBound()
upAnimator(2)
}
/**
* 生成bitmap
*/
fun adjustPhotoRotation(): Bitmap? {
val bm = bitmap ?: return null
val m = Matrix()
m.setRotate(mRotation.toFloat(), bm.width.toFloat() / 2, bm.height.toFloat() / 2)
try {
return Bitmap.createBitmap(bm, 0, 0, bm.width, bm.height, m, true)
} catch (ex: OutOfMemoryError) {
}
return null
}
/**
* 双击手势操作
*/
private inner class CorrectGestureListener : SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent): Boolean {
if (isDoing) return super.onDoubleTap(e)
isDoing = true
val endFactor: Float
val scaleCenterX: Float
val scaleCenterY: Float
if (mScaleFactor == originScale || mScaleFactor == 1f) {
scaleCenterX = 0f
scaleCenterY = 0f
endFactor = originScale * 2
} else {
scaleCenterX = 0f
scaleCenterY = 0f
endFactor = originScale
}
zoomAnimator(mScaleFactor, endFactor, scaleCenterX, scaleCenterY)
return super.onDoubleTap(e)
}
}
private fun zoomAnimator(
startVal: Float,
endVal: Float,
scaleCenterX: Float,
scaleCenterY: Float
) {
if (mScaleAnimator == null) {
newZoomAnimation()
}
if (mScaleAnimator!!.isRunning) {
return
}
val startTranX = translationX
val startTranY = translationY
val scaleHolder = PropertyValuesHolder
.ofFloat(PROPERTY_SCALE, startVal, endVal)
val tranXHolder = PropertyValuesHolder
.ofFloat(PROPERTY_TRANX, startTranX, scaleCenterX)
val tranYHolder = PropertyValuesHolder
.ofFloat(PROPERTY_TRANY, startTranY, scaleCenterY)
mScaleAnimator!!.setValues(scaleHolder, tranXHolder, tranYHolder)
mScaleAnimator!!.duration = DEFAULT_SCALE_DURATION.toLong()
mScaleAnimator!!.start()
}
var mScaleAnimator: ValueAnimator? = null
/**
* 缩放动画
*/
private fun newZoomAnimation() {
mScaleAnimator = ValueAnimator()
mScaleAnimator!!.interpolator = DecelerateInterpolator()
mScaleAnimator!!.addUpdateListener { animation -> //update scaleFactor & tranX & tranY
val scaleValue = animation.getAnimatedValue(PROPERTY_SCALE) as Float
val tranXValue = animation.getAnimatedValue(PROPERTY_TRANX) as Float
val tranYValue = animation.getAnimatedValue(PROPERTY_TRANY) as Float
// if (scaleValue<originScale){
// mScaleFactor = MIN_SCALE;
// }else {
mScaleFactor = scaleValue
// }
scaleX = scaleValue
scaleY = scaleValue
setTranslationXY(tranXValue, tranYValue)
}
mScaleAnimator!!.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {}
override fun onAnimationEnd(animation: Animator) {
isDoing = false
}
override fun onAnimationCancel(animation: Animator) {}
})
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
if (mScaleAnimator != null && mScaleAnimator!!.isRunning) {
mScaleAnimator!!.cancel()
}
}
interface SetRotationListener {
fun setRotation(position: Int, rotation: Int)
}
private var setRotationListener: SetRotationListener? = null
fun setSetRotationListener(listener: SetRotationListener?) {
setRotationListener = listener
}
private var position = 0
fun setPosition(position: Int) {
this.position = position
}
private var tempName //生成需求上传的本地路径,上传成功删除
: String? = null
fun setTempName(tempName: String?) {
this.tempName = tempName
}
/**
* 自己是否不处理
* true 不处理
* false 自己处理
*/
var zoomNotHandler: Boolean? = null
private set
init {
isClickable = true
init(context)
}
companion object {
private const val DEFAULT_SCALE_DURATION = 300
private const val PROPERTY_SCALE = "scale"
private const val PROPERTY_TRANX = "tranX"
private const val PROPERTY_TRANY = "tranY"
}
}