一,概述
本文参考android.view.ScaleGestureDetector,对双指旋转手势做了一层封装,采用了向量计算法简单实现,笔者在此分享下。
二,实例
如下,使用RotateGestureDetector即可委托,实现旋转手势的简单封装,在对应Callback获取到旋转值设置到View即可。
public class RectView extends FrameLayout {
private static final String TAG = "RectView";
private View mRotateView;
private final ScaleGestureDetector scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(@NonNull ScaleGestureDetector detector) {
Log.d(TAG, "onScale() called with: detector = [" + detector.getScaleFactor() + "]");
mRotateView.setScaleX(detector.getScaleFactor());
mRotateView.setScaleY(detector.getScaleFactor());
return true;
}
@Override
public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) {
return super.onScaleBegin(detector);
}
@Override
public void onScaleEnd(@NonNull ScaleGestureDetector detector) {
super.onScaleEnd(detector);
}
});
private final RotateGestureDetector rotateGestureDetector = new RotateGestureDetector(new RotateGestureDetector.Listener() {
@Override
public void onRotateStart(RotateGestureDetector detector) {
}
@Override
public void onRotating(RotateGestureDetector detector) {
mRotateView.setRotation((float) detector.eulerAngle);
}
@Override
public void onRotateEnd(RotateGestureDetector detector) {
}
});
public RectView(Context context) {
super(context);
mRotateView = new View(context);
mRotateView.setBackgroundColor(Color.GRAY);
this.addView(mRotateView, new FrameLayout.LayoutParams(100, 100, Gravity.CENTER));
}
public RectView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public RectView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public RectView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
// @SuppressLint("ClickableViewAccessibility")
// @Override
// public boolean onTouchEvent(MotionEvent event) {
// rotateGestureDetector.onTouchEvent(event);
// return true;
// }
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
rotateGestureDetector.onTouchEvent(ev);
scaleGestureDetector.onTouchEvent(ev);
return true;
}
}
三,实现
Rotate具体实现如下,仅供参考。
import android.view.MotionEvent;
import androidx.annotation.NonNull;
/**
* @author :zhong.jw
* @date :Created in 2023/2/23 13:39
* 旋转手势相关:采用向量法计算角度
*/
public final class RotateGestureDetector {
private static final double DEFAULT_LIMIT_START = 3f;
@NonNull
private final Listener listener;
/**
* 是否旋转中
*/
public boolean isRotating = false;
/**
* 旋转轴点x
*/
public int focusX;
/**
* 旋转轴点y
*/
public int focusY;
/**
* 欧拉角,范围[-180~180]
*/
public double eulerAngle = 0;
/**
* 弧度
*/
public double radian = 0;
/**
* 开始旋转的初始向量x值
*/
public double x1 = 0;
/**
* 开始旋转的初始向量y值
*/
public double y1 = 0;
/**
* 开始旋转的初始向量斜率
*/
public double k1 = 0;
public RotateGestureDetector(@NonNull Listener listener) {
this.listener = listener;
}
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
int pointCount = event.getPointerCount();
if ((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) && (pointCount == 2) && !isRotating) {
double e1x = event.getX(0);
double e2x = event.getX(1);
double e1y = event.getY(0);
double e2y = event.getY(1);
focusX = (int) (e1x + e2x) / 2;
focusY = (int) ((e1y + e2y) / 2);
x1 = e2x - e1x;
y1 = e2y - e1y;
k1 = y1 / x1;
return true;
}
if (action == MotionEvent.ACTION_MOVE && pointCount == 2) {
double e1x = event.getX(0);
double e2x = event.getX(1);
double e1y = event.getY(0);
double e2y = event.getY(1);
double x2 = e2x - e1x;
double y2 = e2y - e1y;
//angle = arccos(ab/(|a||b|))
radian = Math.acos((x1 * x2 + y1 * y2) / (Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2))));
// y = k1*x2 > y2 来判断是否属于外角
eulerAngle = (radian / Math.PI * 180) * (k1 * x2 > y2 ? -1 : 1);
if (isRotating) {
listener.onRotating(this);
}
if (Math.abs(eulerAngle) >= DEFAULT_LIMIT_START) {
isRotating = true;
listener.onRotateStart(this);
}
return true;
}
if ((action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) && isRotating) {
isRotating = false;
listener.onRotateEnd(this);
}
return true;
}
public interface Listener {
/**
* @param detector:旋转信息
*/
void onRotateStart(RotateGestureDetector detector);
/**
* @param detector:旋转信息
*/
void onRotating(RotateGestureDetector detector);
/**
* @param detector:旋转信息
*/
void onRotateEnd(RotateGestureDetector detector);
}
}