Android: implement gesture container
This commit is contained in:
parent
ac6a027f02
commit
50d9377960
@ -42,6 +42,7 @@ import pub.doric.plugin.StatusBarPlugin;
|
||||
import pub.doric.plugin.StoragePlugin;
|
||||
import pub.doric.refresh.RefreshableNode;
|
||||
import pub.doric.shader.DraggableNode;
|
||||
import pub.doric.shader.GestureContainerNode;
|
||||
import pub.doric.shader.HLayoutNode;
|
||||
import pub.doric.shader.ImageNode;
|
||||
import pub.doric.shader.InputNode;
|
||||
@ -121,6 +122,7 @@ public class DoricRegistry {
|
||||
this.registerViewNode(DraggableNode.class);
|
||||
this.registerViewNode(SwitchNode.class);
|
||||
this.registerViewNode(FlexNode.class);
|
||||
this.registerViewNode(GestureContainerNode.class);
|
||||
initRegistry(this);
|
||||
doricJSEngine.setEnvironmentValue(DoricSingleton.getInstance().envMap);
|
||||
DoricSingleton.getInstance().registries.add(new WeakReference<>(this));
|
||||
|
@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright [2021] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.shader
|
||||
* @Author: jingpeng.wang
|
||||
* @CreateDate: 2021-09-16
|
||||
*/
|
||||
|
||||
@DoricPlugin(name = "GestureContainer")
|
||||
public class GestureContainerNode extends StackNode {
|
||||
|
||||
private enum SwipeOrientation {
|
||||
LEFT(0), RIGHT(1), TOP(2), BOTTOM(3);
|
||||
|
||||
private final int value;
|
||||
|
||||
SwipeOrientation(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private String onSingleTap;
|
||||
private String onDoubleTap;
|
||||
private String onLongPress;
|
||||
private String onPinch;
|
||||
private String onPan;
|
||||
private String onRotate;
|
||||
private String onSwipe;
|
||||
|
||||
public GestureContainerNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FrameLayout build() {
|
||||
return new GestureContainerView(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||
if ("onSingleTap".equals(name)) {
|
||||
if (prop.isString()) {
|
||||
onSingleTap = prop.asString().value();
|
||||
} else {
|
||||
onSingleTap = null;
|
||||
}
|
||||
} else if ("onDoubleTap".equals(name)) {
|
||||
if (prop.isString()) {
|
||||
onDoubleTap = prop.asString().value();
|
||||
} else {
|
||||
onDoubleTap = null;
|
||||
}
|
||||
} else if ("onLongPress".equals(name)) {
|
||||
if (prop.isString()) {
|
||||
onLongPress = prop.asString().value();
|
||||
} else {
|
||||
onLongPress = null;
|
||||
}
|
||||
} else if ("onPinch".equals(name)) {
|
||||
if (prop.isString()) {
|
||||
onPinch = prop.asString().value();
|
||||
} else {
|
||||
onPinch = null;
|
||||
}
|
||||
} else if ("onPan".equals(name)) {
|
||||
if (prop.isString()) {
|
||||
onPan = prop.asString().value();
|
||||
} else {
|
||||
onPan = null;
|
||||
}
|
||||
} else if ("onRotate".equals(name)) {
|
||||
if (prop.isString()) {
|
||||
onRotate = prop.asString().value();
|
||||
} else {
|
||||
onRotate = null;
|
||||
}
|
||||
} else if ("onSwipe".equals(name)) {
|
||||
if (prop.isString()) {
|
||||
onSwipe = prop.asString().value();
|
||||
} else {
|
||||
onSwipe = null;
|
||||
}
|
||||
} else {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
private class GestureContainerView extends FrameLayout {
|
||||
private final GestureDetector gestureDetector;
|
||||
private final ScaleGestureDetector scaleGestureDetector;
|
||||
private final RotateGestureDetector rotateGestureDetector;
|
||||
|
||||
public GestureContainerView(Context context) {
|
||||
super(context);
|
||||
|
||||
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
private static final int SWIPE_THRESHOLD = 100;
|
||||
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
if (onSingleTap != null)
|
||||
callJSResponse(onSingleTap);
|
||||
return super.onSingleTapConfirmed(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
if (onDoubleTap != null)
|
||||
callJSResponse(onDoubleTap);
|
||||
return super.onDoubleTap(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
super.onLongPress(e);
|
||||
if (onLongPress != null)
|
||||
callJSResponse(onLongPress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
if (scaleGestureDetector.isInProgress()) {
|
||||
// don't allow scrolling while scaling
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle scrolling
|
||||
if (onPan != null)
|
||||
callJSResponse(onPan, DoricUtils.px2dp(distanceX), DoricUtils.px2dp(distanceY));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
boolean result = false;
|
||||
try {
|
||||
float diffY = e2.getY() - e1.getY();
|
||||
float diffX = e2.getX() - e1.getX();
|
||||
if (Math.abs(diffX) > Math.abs(diffY)) {
|
||||
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffX > 0) {
|
||||
onSwipeRight();
|
||||
} else {
|
||||
onSwipeLeft();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
} else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffY > 0) {
|
||||
onSwipeBottom();
|
||||
} else {
|
||||
onSwipeTop();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.OnScaleGestureListener() {
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
|
||||
if (onPinch != null)
|
||||
callJSResponse(onPinch, scaleGestureDetector.getScaleFactor());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureDetector.OnRotateGestureListener() {
|
||||
|
||||
@Override
|
||||
public boolean onRotate(float degrees, float focusX, float focusY) {
|
||||
if (onRotate != null)
|
||||
callJSResponse(onRotate, degrees * Math.PI / 180f);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// handle touch event conflict when in a scroll view or other similar containers
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
getParent().requestDisallowInterceptTouchEvent(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// handle gesture
|
||||
boolean scaleRet = scaleGestureDetector.onTouchEvent(event);
|
||||
boolean rotateRet = rotateGestureDetector.onTouchEvent(event);
|
||||
boolean commonRet = gestureDetector.onTouchEvent(event);
|
||||
return (scaleRet || rotateRet || commonRet) || super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
private void onSwipeLeft() {
|
||||
if (onSwipe != null)
|
||||
callJSResponse(onSwipe, SwipeOrientation.LEFT.value);
|
||||
}
|
||||
|
||||
private void onSwipeRight() {
|
||||
if (onSwipe != null)
|
||||
callJSResponse(onSwipe, SwipeOrientation.RIGHT.value);
|
||||
}
|
||||
|
||||
private void onSwipeTop() {
|
||||
if (onSwipe != null)
|
||||
callJSResponse(onSwipe, SwipeOrientation.TOP.value);
|
||||
}
|
||||
|
||||
private void onSwipeBottom() {
|
||||
if (onSwipe != null)
|
||||
callJSResponse(onSwipe, SwipeOrientation.BOTTOM.value);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright [2021] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.shader
|
||||
* @Author: jingpeng.wang
|
||||
* @CreateDate: 2021-09-16
|
||||
*/
|
||||
|
||||
public class RotateGestureDetector {
|
||||
public interface OnRotateGestureListener {
|
||||
boolean onRotate(float degrees, float focusX, float focusY);
|
||||
}
|
||||
|
||||
public static class SimpleOnRotateGestureDetector implements OnRotateGestureListener {
|
||||
@Override
|
||||
public boolean onRotate(float degrees, float focusX, float focusY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static float RADIAN_TO_DEGREES = (float) (180.0 / Math.PI);
|
||||
private OnRotateGestureListener listener;
|
||||
private float prevX = 0.0f;
|
||||
private float prevY = 0.0f;
|
||||
private float prevTan;
|
||||
|
||||
public RotateGestureDetector(Context context, OnRotateGestureListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event.getPointerCount() == 2 && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
|
||||
boolean result = true;
|
||||
float x = event.getX(1) - event.getX(0);
|
||||
float y = event.getY(1) - event.getY(0);
|
||||
float focusX = (event.getX(1) + event.getX(0)) * 0.5f;
|
||||
float focusY = (event.getY(1) + event.getY(0)) * 0.5f;
|
||||
float tan = (float) Math.atan2(y, x);
|
||||
|
||||
if (prevX != 0.0f && prevY != 0.0f) {
|
||||
result = listener.onRotate((tan - prevTan) * RADIAN_TO_DEGREES, focusX, focusY);
|
||||
}
|
||||
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
prevTan = tan;
|
||||
return result;
|
||||
} else {
|
||||
prevX = prevY = prevTan = 0.0f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user