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.plugin.StoragePlugin;
|
||||||
import pub.doric.refresh.RefreshableNode;
|
import pub.doric.refresh.RefreshableNode;
|
||||||
import pub.doric.shader.DraggableNode;
|
import pub.doric.shader.DraggableNode;
|
||||||
|
import pub.doric.shader.GestureContainerNode;
|
||||||
import pub.doric.shader.HLayoutNode;
|
import pub.doric.shader.HLayoutNode;
|
||||||
import pub.doric.shader.ImageNode;
|
import pub.doric.shader.ImageNode;
|
||||||
import pub.doric.shader.InputNode;
|
import pub.doric.shader.InputNode;
|
||||||
@ -121,6 +122,7 @@ public class DoricRegistry {
|
|||||||
this.registerViewNode(DraggableNode.class);
|
this.registerViewNode(DraggableNode.class);
|
||||||
this.registerViewNode(SwitchNode.class);
|
this.registerViewNode(SwitchNode.class);
|
||||||
this.registerViewNode(FlexNode.class);
|
this.registerViewNode(FlexNode.class);
|
||||||
|
this.registerViewNode(GestureContainerNode.class);
|
||||||
initRegistry(this);
|
initRegistry(this);
|
||||||
doricJSEngine.setEnvironmentValue(DoricSingleton.getInstance().envMap);
|
doricJSEngine.setEnvironmentValue(DoricSingleton.getInstance().envMap);
|
||||||
DoricSingleton.getInstance().registries.add(new WeakReference<>(this));
|
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