mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-21 06:15:15 +00:00
add touch responder system on android
This commit is contained in:
66
Example/android/app/BUCK
Normal file
66
Example/android/app/BUCK
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||||
|
# To run your application with Buck:
|
||||||
|
# - install Buck
|
||||||
|
# - `npm start` - to start the packager
|
||||||
|
# - `cd android`
|
||||||
|
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US`
|
||||||
|
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||||
|
# - `buck install -r android/app` - compile, install and run application
|
||||||
|
#
|
||||||
|
|
||||||
|
lib_deps = []
|
||||||
|
for jarfile in glob(['libs/*.jar']):
|
||||||
|
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
|
||||||
|
lib_deps.append(':' + name)
|
||||||
|
prebuilt_jar(
|
||||||
|
name = name,
|
||||||
|
binary_jar = jarfile,
|
||||||
|
)
|
||||||
|
|
||||||
|
for aarfile in glob(['libs/*.aar']):
|
||||||
|
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
|
||||||
|
lib_deps.append(':' + name)
|
||||||
|
android_prebuilt_aar(
|
||||||
|
name = name,
|
||||||
|
aar = aarfile,
|
||||||
|
)
|
||||||
|
|
||||||
|
android_library(
|
||||||
|
name = 'all-libs',
|
||||||
|
exported_deps = lib_deps
|
||||||
|
)
|
||||||
|
|
||||||
|
android_library(
|
||||||
|
name = 'app-code',
|
||||||
|
srcs = glob([
|
||||||
|
'src/main/java/**/*.java',
|
||||||
|
]),
|
||||||
|
deps = [
|
||||||
|
':all-libs',
|
||||||
|
':build_config',
|
||||||
|
':res',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
android_build_config(
|
||||||
|
name = 'build_config',
|
||||||
|
package = 'com.artsvgexample',
|
||||||
|
)
|
||||||
|
|
||||||
|
android_resource(
|
||||||
|
name = 'res',
|
||||||
|
res = 'src/main/res',
|
||||||
|
package = 'com.artsvgexample',
|
||||||
|
)
|
||||||
|
|
||||||
|
android_binary(
|
||||||
|
name = 'app',
|
||||||
|
package_type = 'debug',
|
||||||
|
manifest = 'src/main/AndroidManifest.xml',
|
||||||
|
keystore = '//android/keystores:debug',
|
||||||
|
deps = [
|
||||||
|
':app-code',
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -9,7 +9,7 @@ import com.android.build.OutputFile
|
|||||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||||
* bundle directly from the development server. Below you can see all the possible configurations
|
* bundle directly from the development server. Below you can see all the possible configurations
|
||||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||||
* `apply from: "react.gradle"` line.
|
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||||
*
|
*
|
||||||
* project.ext.react = [
|
* project.ext.react = [
|
||||||
* // the name of the generated asset file containing your JS bundle
|
* // the name of the generated asset file containing your JS bundle
|
||||||
@@ -26,7 +26,9 @@ import com.android.build.OutputFile
|
|||||||
*
|
*
|
||||||
* // whether to bundle JS and assets in another build variant (if configured).
|
* // whether to bundle JS and assets in another build variant (if configured).
|
||||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||||
* // The configuration property is in the format 'bundleIn${productFlavor}${buildType}'
|
* // The configuration property can be in the following formats
|
||||||
|
* // 'bundleIn${productFlavor}${buildType}'
|
||||||
|
* // 'bundleIn${buildType}'
|
||||||
* // bundleInFreeDebug: true,
|
* // bundleInFreeDebug: true,
|
||||||
* // bundleInPaidRelease: true,
|
* // bundleInPaidRelease: true,
|
||||||
* // bundleInBeta: true,
|
* // bundleInBeta: true,
|
||||||
@@ -57,11 +59,10 @@ import com.android.build.OutputFile
|
|||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
apply from: "react.gradle"
|
apply from: "../../node_modules/react-native/react.gradle"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this to true to create three separate APKs instead of one:
|
* Set this to true to create two separate APKs instead of one:
|
||||||
* - A universal APK that works on all devices
|
|
||||||
* - An APK that only works on ARM devices
|
* - An APK that only works on ARM devices
|
||||||
* - An APK that only works on x86 devices
|
* - An APK that only works on x86 devices
|
||||||
* The advantage is the size of the APK is reduced by about 4MB.
|
* The advantage is the size of the APK is reduced by about 4MB.
|
||||||
@@ -91,9 +92,9 @@ android {
|
|||||||
}
|
}
|
||||||
splits {
|
splits {
|
||||||
abi {
|
abi {
|
||||||
enable enableSeparateBuildPerCPUArchitecture
|
|
||||||
universalApk false
|
|
||||||
reset()
|
reset()
|
||||||
|
enable enableSeparateBuildPerCPUArchitecture
|
||||||
|
universalApk false // If true, also generate a universal APK
|
||||||
include "armeabi-v7a", "x86"
|
include "armeabi-v7a", "x86"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,9 +120,15 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compile project(':react-native-svg')
|
||||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||||
compile "com.android.support:appcompat-v7:23.0.1"
|
compile "com.android.support:appcompat-v7:23.0.1"
|
||||||
compile "com.facebook.react:react-native:0.20.+"
|
compile "com.facebook.react:react-native:+" // From node_modules
|
||||||
|
}
|
||||||
compile project(':react-native-svg')
|
|
||||||
|
// Run this once to be able to run the application with BUCK
|
||||||
|
// puts all compile dependencies into folder libs for BUCK to use
|
||||||
|
task copyDownloadableDepsToLibs(type: Copy) {
|
||||||
|
from configurations.compile
|
||||||
|
into 'libs'
|
||||||
}
|
}
|
||||||
|
|||||||
4
Example/android/app/proguard-rules.pro
vendored
4
Example/android/app/proguard-rules.pro
vendored
@@ -61,7 +61,3 @@
|
|||||||
-dontwarn java.nio.file.*
|
-dontwarn java.nio.file.*
|
||||||
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||||
-dontwarn okio.**
|
-dontwarn okio.**
|
||||||
|
|
||||||
# stetho
|
|
||||||
|
|
||||||
-dontwarn com.facebook.stetho.**
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.artsvgexample;
|
package com.artsvgexample;
|
||||||
|
|
||||||
import com.facebook.react.ReactActivity;
|
import com.facebook.react.ReactActivity;
|
||||||
|
import com.horcrux.svg.RNSvgPackage;
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
import com.facebook.react.shell.MainReactPackage;
|
import com.facebook.react.shell.MainReactPackage;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.horcrux.svg.RNSvgPackage;
|
|
||||||
|
|
||||||
public class MainActivity extends ReactActivity {
|
public class MainActivity extends ReactActivity {
|
||||||
|
|
||||||
@@ -28,15 +28,15 @@ public class MainActivity extends ReactActivity {
|
|||||||
return BuildConfig.DEBUG;
|
return BuildConfig.DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of packages used by the app. If the app uses additional views
|
* A list of packages used by the app. If the app uses additional views
|
||||||
* or modules besides the default ones, add more packages here.
|
* or modules besides the default ones, add more packages here.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected List<ReactPackage> getPackages() {
|
protected List<ReactPackage> getPackages() {
|
||||||
return Arrays.<ReactPackage>asList(
|
return Arrays.<ReactPackage>asList(
|
||||||
new RNSvgPackage(),
|
new MainReactPackage(),
|
||||||
new MainReactPackage()
|
new RNSvgPackage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,5 +16,9 @@ allprojects {
|
|||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
maven {
|
||||||
|
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||||
|
url "$projectDir/../../node_modules/react-native/android"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
rootProject.name = 'ArtSvgExample'
|
rootProject.name = 'ArtSvgExample'
|
||||||
|
|
||||||
include ':app'
|
include ':app'
|
||||||
|
|
||||||
include ':react-native-svg'
|
include ':react-native-svg'
|
||||||
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
|
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
|
||||||
|
|||||||
@@ -62,19 +62,14 @@ public class RNSVGCircleShadowNode extends RNSVGPathShadowNode {
|
|||||||
@Override
|
@Override
|
||||||
protected Path getPath(Canvas canvas, Paint paint) {
|
protected Path getPath(Canvas canvas, Paint paint) {
|
||||||
Path path = new Path();
|
Path path = new Path();
|
||||||
|
float cx = PropHelper.fromPercentageToFloat(mCx, mWidth, 0, mScale);
|
||||||
Rect box = canvas.getClipBounds();
|
float cy = PropHelper.fromPercentageToFloat(mCy, mHeight, 0, mScale);
|
||||||
float height = box.height();
|
|
||||||
float width = box.width();
|
|
||||||
|
|
||||||
float cx = PropHelper.fromPercentageToFloat(mCx, width, 0, mScale);
|
|
||||||
float cy = PropHelper.fromPercentageToFloat(mCy, height, 0, mScale);
|
|
||||||
|
|
||||||
float r;
|
float r;
|
||||||
if (PropHelper.isPercentage(mR)) {
|
if (PropHelper.isPercentage(mR)) {
|
||||||
r = PropHelper.fromPercentageToFloat(mR, 1, 0, 1);
|
r = PropHelper.fromPercentageToFloat(mR, 1, 0, 1);
|
||||||
float powX = (float)Math.pow((width * r), 2);
|
float powX = (float)Math.pow((mWidth * r), 2);
|
||||||
float powY = (float)Math.pow((height * r), 2);
|
float powY = (float)Math.pow((mHeight * r), 2);
|
||||||
r = (float)Math.sqrt(powX + powY) / (float)Math.sqrt(2);
|
r = (float)Math.sqrt(powX + powY) / (float)Math.sqrt(2);
|
||||||
} else {
|
} else {
|
||||||
r = Float.parseFloat(mR) * mScale;
|
r = Float.parseFloat(mR) * mScale;
|
||||||
|
|||||||
@@ -65,15 +65,11 @@ public class RNSVGEllipseShadowNode extends RNSVGPathShadowNode {
|
|||||||
protected Path getPath(Canvas canvas, Paint paint) {
|
protected Path getPath(Canvas canvas, Paint paint) {
|
||||||
Path path = new Path();
|
Path path = new Path();
|
||||||
|
|
||||||
Rect box = canvas.getClipBounds();
|
|
||||||
float height = box.height();
|
|
||||||
float width = box.width();
|
|
||||||
|
|
||||||
// draw ellipse
|
// draw ellipse
|
||||||
float cx = PropHelper.fromPercentageToFloat(mCx, width, 0, mScale);
|
float cx = PropHelper.fromPercentageToFloat(mCx, mWidth, 0, mScale);
|
||||||
float cy = PropHelper.fromPercentageToFloat(mCy, height, 0, mScale);
|
float cy = PropHelper.fromPercentageToFloat(mCy, mHeight, 0, mScale);
|
||||||
float rx = PropHelper.fromPercentageToFloat(mRx, width, 0, mScale);
|
float rx = PropHelper.fromPercentageToFloat(mRx, mWidth, 0, mScale);
|
||||||
float ry = PropHelper.fromPercentageToFloat(mRy, height, 0, mScale);
|
float ry = PropHelper.fromPercentageToFloat(mRy, mHeight, 0, mScale);
|
||||||
RectF oval = new RectF(cx - rx, cy - ry, cx + rx, cy + ry);
|
RectF oval = new RectF(cx - rx, cy - ry, cx + rx, cy + ry);
|
||||||
path.addOval(oval, Path.Direction.CW);
|
path.addOval(oval, Path.Direction.CW);
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ package com.horcrux.svg;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||||
|
|
||||||
@@ -29,12 +32,6 @@ public class RNSVGGroupShadowNode extends RNSVGVirtualNode {
|
|||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVirtual() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void draw(Canvas canvas, Paint paint, float opacity) {
|
public void draw(Canvas canvas, Paint paint, float opacity) {
|
||||||
opacity *= mOpacity;
|
opacity *= mOpacity;
|
||||||
if (opacity > MIN_OPACITY_FOR_DRAW) {
|
if (opacity > MIN_OPACITY_FOR_DRAW) {
|
||||||
@@ -43,6 +40,7 @@ public class RNSVGGroupShadowNode extends RNSVGVirtualNode {
|
|||||||
if (mAsClipPath == null) {
|
if (mAsClipPath == null) {
|
||||||
for (int i = 0; i < getChildCount(); i++) {
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i);
|
RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i);
|
||||||
|
child.setDimensions(mWidth, mHeight);
|
||||||
child.draw(canvas, paint, opacity);
|
child.draw(canvas, paint, opacity);
|
||||||
child.markUpdateSeen();
|
child.markUpdateSeen();
|
||||||
}
|
}
|
||||||
@@ -62,4 +60,23 @@ public class RNSVGGroupShadowNode extends RNSVGVirtualNode {
|
|||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hitTest(Point point, View view) {
|
||||||
|
// TODO: run hit test only if necessary
|
||||||
|
// TODO: ClipPath never run hitTest
|
||||||
|
if (mClipPathId == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int viewTag = -1;
|
||||||
|
for (int i = getChildCount() - 1; i >= 0; i--) {
|
||||||
|
viewTag = ((RNSVGVirtualNode) getChildAt(i)).hitTest(point, ((ViewGroup) view).getChildAt(i));
|
||||||
|
if (viewTag != -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewTag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
|
|||||||
bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
|
bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Log.e("URI", "" + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
@@ -131,6 +132,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
|
|||||||
@Override
|
@Override
|
||||||
public void draw(Canvas canvas, Paint paint, float opacity) {
|
public void draw(Canvas canvas, Paint paint, float opacity) {
|
||||||
canvas.saveLayer(0f, 0f, 0f, 0f, paint, Canvas.ALL_SAVE_FLAG);
|
canvas.saveLayer(0f, 0f, 0f, 0f, paint, Canvas.ALL_SAVE_FLAG);
|
||||||
|
Log.e("Count", "" + canvas.getSaveCount());
|
||||||
loadBitmap(getResourceDrawableId(getThemedContext(), null), canvas, paint);
|
loadBitmap(getResourceDrawableId(getThemedContext(), null), canvas, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,14 +69,10 @@ public class RNSVGLineShadowNode extends RNSVGPathShadowNode {
|
|||||||
protected Path getPath(Canvas canvas, Paint paint) {
|
protected Path getPath(Canvas canvas, Paint paint) {
|
||||||
Path path = new Path();
|
Path path = new Path();
|
||||||
|
|
||||||
Rect box = canvas.getClipBounds();
|
float x1 = PropHelper.fromPercentageToFloat(mX1, mWidth, 0, mScale);
|
||||||
float height = box.height();
|
float y1 = PropHelper.fromPercentageToFloat(mY1, mHeight, 0, mScale);
|
||||||
float width = box.width();
|
float x2 = PropHelper.fromPercentageToFloat(mX2, mWidth, 0, mScale);
|
||||||
|
float y2 = PropHelper.fromPercentageToFloat(mY2, mHeight, 0, mScale);
|
||||||
float x1 = PropHelper.fromPercentageToFloat(mX1, width, 0, mScale);
|
|
||||||
float y1 = PropHelper.fromPercentageToFloat(mY1, height, 0, mScale);
|
|
||||||
float x2 = PropHelper.fromPercentageToFloat(mX2, width, 0, mScale);
|
|
||||||
float y2 = PropHelper.fromPercentageToFloat(mY2, height, 0, mScale);
|
|
||||||
|
|
||||||
path.moveTo(x1, y1);
|
path.moveTo(x1, y1);
|
||||||
path.lineTo(x2, y2);
|
path.lineTo(x2, y2);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package com.horcrux.svg;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.DashPathEffect;
|
import android.graphics.DashPathEffect;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
@@ -26,6 +27,8 @@ import android.graphics.Region;
|
|||||||
import android.graphics.Shader;
|
import android.graphics.Shader;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.facebook.common.logging.FLog;
|
import com.facebook.common.logging.FLog;
|
||||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||||
@@ -53,14 +56,14 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
private @Nullable float[] mStrokeDasharray;
|
private @Nullable float[] mStrokeDasharray;
|
||||||
private float mStrokeWidth = 1;
|
private float mStrokeWidth = 1;
|
||||||
private float mStrokeDashoffset = 0;
|
private float mStrokeDashoffset = 0;
|
||||||
private int mStrokeLinecap = CAP_ROUND;
|
private Paint.Cap mStrokeLinecap = Paint.Cap.ROUND;
|
||||||
private int mStrokeLinejoin = JOIN_ROUND;
|
private Paint.Join mStrokeLinejoin = Paint.Join.ROUND;
|
||||||
private int mFillRule = FILL_RULE_NONZERO;
|
private Path.FillType mFillRule = Path.FillType.WINDING;
|
||||||
private boolean mFillRuleSet;
|
private boolean mFillRuleSet;
|
||||||
protected Path mPath;
|
protected Path mPath;
|
||||||
private boolean mPathSet;
|
private boolean mPathSet;
|
||||||
private float[] mD;
|
private float[] mD;
|
||||||
private Point mPaint;
|
|
||||||
|
|
||||||
@ReactProp(name = "d")
|
@ReactProp(name = "d")
|
||||||
public void setPath(@Nullable ReadableArray shapePath) {
|
public void setPath(@Nullable ReadableArray shapePath) {
|
||||||
@@ -79,7 +82,17 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
|
|
||||||
@ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO)
|
@ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO)
|
||||||
public void setFillRule(int fillRule) {
|
public void setFillRule(int fillRule) {
|
||||||
mFillRule = fillRule;
|
switch (fillRule) {
|
||||||
|
case FILL_RULE_EVENODD:
|
||||||
|
mFillRule = Path.FillType.EVEN_ODD;
|
||||||
|
break;
|
||||||
|
case FILL_RULE_NONZERO:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new JSApplicationIllegalArgumentException(
|
||||||
|
"fillRule " + mFillRule + " unrecognized");
|
||||||
|
}
|
||||||
|
|
||||||
mFillRuleSet = true;
|
mFillRuleSet = true;
|
||||||
setupPath();
|
setupPath();
|
||||||
markUpdated();
|
markUpdated();
|
||||||
@@ -117,13 +130,39 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
|
|
||||||
@ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND)
|
@ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND)
|
||||||
public void setStrokeLinecap(int strokeLinecap) {
|
public void setStrokeLinecap(int strokeLinecap) {
|
||||||
mStrokeLinecap = strokeLinecap;
|
switch (strokeLinecap) {
|
||||||
|
case CAP_BUTT:
|
||||||
|
mStrokeLinecap = Paint.Cap.BUTT;
|
||||||
|
break;
|
||||||
|
case CAP_SQUARE:
|
||||||
|
mStrokeLinecap = Paint.Cap.SQUARE;
|
||||||
|
break;
|
||||||
|
case CAP_ROUND:
|
||||||
|
mStrokeLinecap = Paint.Cap.ROUND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new JSApplicationIllegalArgumentException(
|
||||||
|
"strokeLinecap " + mStrokeLinecap + " unrecognized");
|
||||||
|
}
|
||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND)
|
@ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND)
|
||||||
public void setStrokeLinejoin(int strokeLinejoin) {
|
public void setStrokeLinejoin(int strokeLinejoin) {
|
||||||
mStrokeLinejoin = strokeLinejoin;
|
switch (strokeLinejoin) {
|
||||||
|
case JOIN_MITER:
|
||||||
|
mStrokeLinejoin = Paint.Join.MITER;
|
||||||
|
break;
|
||||||
|
case JOIN_BEVEL:
|
||||||
|
mStrokeLinejoin = Paint.Join.BEVEL;
|
||||||
|
break;
|
||||||
|
case JOIN_ROUND:
|
||||||
|
mStrokeLinejoin = Paint.Join.ROUND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new JSApplicationIllegalArgumentException(
|
||||||
|
"strokeLinejoin " + mStrokeLinejoin + " unrecognized");
|
||||||
|
}
|
||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +217,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up {@link #mPaint} according to the props set on a shadow view. Returns {@code true}
|
* Sets up paint according to the props set on a shadow view. Returns {@code true}
|
||||||
* if the fill should be drawn, {@code false} if not.
|
* if the fill should be drawn, {@code false} if not.
|
||||||
*/
|
*/
|
||||||
protected boolean setupFillPaint(Paint paint, float opacity, @Nullable RectF box) {
|
protected boolean setupFillPaint(Paint paint, float opacity, @Nullable RectF box) {
|
||||||
@@ -193,7 +232,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up {@link #mPaint} according to the props set on a shadow view. Returns {@code true}
|
* Sets up paint according to the props set on a shadow view. Returns {@code true}
|
||||||
* if the stroke should be drawn, {@code false} if not.
|
* if the stroke should be drawn, {@code false} if not.
|
||||||
*/
|
*/
|
||||||
protected boolean setupStrokePaint(Paint paint, float opacity, @Nullable RectF box) {
|
protected boolean setupStrokePaint(Paint paint, float opacity, @Nullable RectF box) {
|
||||||
@@ -203,35 +242,8 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
paint.reset();
|
paint.reset();
|
||||||
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
switch (mStrokeLinecap) {
|
paint.setStrokeCap(mStrokeLinecap);
|
||||||
case CAP_BUTT:
|
paint.setStrokeJoin(mStrokeLinejoin);
|
||||||
paint.setStrokeCap(Paint.Cap.BUTT);
|
|
||||||
break;
|
|
||||||
case CAP_SQUARE:
|
|
||||||
paint.setStrokeCap(Paint.Cap.SQUARE);
|
|
||||||
break;
|
|
||||||
case CAP_ROUND:
|
|
||||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new JSApplicationIllegalArgumentException(
|
|
||||||
"strokeLinecap " + mStrokeLinecap + " unrecognized");
|
|
||||||
}
|
|
||||||
switch (mStrokeLinejoin) {
|
|
||||||
case JOIN_MITER:
|
|
||||||
paint.setStrokeJoin(Paint.Join.MITER);
|
|
||||||
break;
|
|
||||||
case JOIN_BEVEL:
|
|
||||||
paint.setStrokeJoin(Paint.Join.BEVEL);
|
|
||||||
break;
|
|
||||||
case JOIN_ROUND:
|
|
||||||
paint.setStrokeJoin(Paint.Join.ROUND);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new JSApplicationIllegalArgumentException(
|
|
||||||
"strokeLinejoin " + mStrokeLinejoin + " unrecognized");
|
|
||||||
}
|
|
||||||
|
|
||||||
paint.setStrokeWidth(mStrokeWidth * mScale);
|
paint.setStrokeWidth(mStrokeWidth * mScale);
|
||||||
setupPaint(paint, opacity, mStrokeColor, box);
|
setupPaint(paint, opacity, mStrokeColor, box);
|
||||||
|
|
||||||
@@ -312,21 +324,67 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
// TODO: Support pattern.
|
// TODO: Support pattern.
|
||||||
FLog.w(ReactConstants.TAG, "RNSVG: Color type " + colorType + " not supported!");
|
FLog.w(ReactConstants.TAG, "RNSVG: Color type " + colorType + " not supported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Path getPath(@Nullable Canvas canvas, @Nullable Paint paint) {
|
protected Path getPath(@Nullable Canvas canvas, @Nullable Paint paint) {
|
||||||
Path path = new Path();
|
Path path = new Path();
|
||||||
switch (mFillRule) {
|
path.setFillType(mFillRule);
|
||||||
case FILL_RULE_EVENODD:
|
|
||||||
path.setFillType(Path.FillType.EVEN_ODD);
|
|
||||||
break;
|
|
||||||
case FILL_RULE_NONZERO:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new JSApplicationIllegalArgumentException(
|
|
||||||
"fillRule " + mFillRule + " unrecognized");
|
|
||||||
}
|
|
||||||
super.createPath(mD, path);
|
super.createPath(mD, path);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hitTest(Point point, View view) {
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(
|
||||||
|
(int) mWidth,
|
||||||
|
(int) mHeight,
|
||||||
|
Bitmap.Config.ARGB_8888);
|
||||||
|
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
canvas.concat(mMatrix);
|
||||||
|
|
||||||
|
Paint paint = new Paint();
|
||||||
|
clip(canvas, paint);
|
||||||
|
setHitTestFill(paint);
|
||||||
|
canvas.drawPath(mPath, paint);
|
||||||
|
|
||||||
|
if (setHitTestStroke(paint)) {
|
||||||
|
canvas.drawPath(mPath, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.setBitmap(bitmap);
|
||||||
|
try {
|
||||||
|
if (bitmap.getPixel(point.x, point.y) != 0) {
|
||||||
|
return view.getId();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return -1;
|
||||||
|
} finally {
|
||||||
|
bitmap.recycle();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setHitTestFill(Paint paint) {
|
||||||
|
paint.reset();
|
||||||
|
paint.setARGB(255, 0, 0, 0);
|
||||||
|
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setStyle(Paint.Style.FILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean setHitTestStroke(Paint paint) {
|
||||||
|
if (mStrokeWidth == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
paint.reset();
|
||||||
|
paint.setARGB(255, 0, 0, 0);
|
||||||
|
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
paint.setStrokeWidth(mStrokeWidth * mScale);
|
||||||
|
paint.setStrokeCap(mStrokeLinecap);
|
||||||
|
paint.setStrokeJoin(mStrokeLinejoin);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ public class RNSVGRectShadowNode extends RNSVGPathShadowNode {
|
|||||||
|
|
||||||
private String mY;
|
private String mY;
|
||||||
|
|
||||||
private String mWidth;
|
private String mW;
|
||||||
|
|
||||||
private String mHeight;
|
private String mH;
|
||||||
|
|
||||||
private String mRx;
|
private String mRx;
|
||||||
|
|
||||||
@@ -54,14 +54,14 @@ public class RNSVGRectShadowNode extends RNSVGPathShadowNode {
|
|||||||
|
|
||||||
@ReactProp(name = "width")
|
@ReactProp(name = "width")
|
||||||
public void setWidth(String width) {
|
public void setWidth(String width) {
|
||||||
mWidth = width;
|
mW = width;
|
||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ReactProp(name = "height")
|
@ReactProp(name = "height")
|
||||||
public void setHeight(String height) {
|
public void setHeight(String height) {
|
||||||
mHeight = height;
|
mH = height;
|
||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,16 +88,12 @@ public class RNSVGRectShadowNode extends RNSVGPathShadowNode {
|
|||||||
protected Path getPath(Canvas canvas, Paint paint) {
|
protected Path getPath(Canvas canvas, Paint paint) {
|
||||||
Path path = new Path();
|
Path path = new Path();
|
||||||
|
|
||||||
Rect box = canvas.getClipBounds();
|
float x = PropHelper.fromPercentageToFloat(mX, mWidth, 0, mScale);
|
||||||
float height = box.height();
|
float y = PropHelper.fromPercentageToFloat(mY, mHeight, 0, mScale);
|
||||||
float width = box.width();
|
float w = PropHelper.fromPercentageToFloat(mW, mWidth, 0, mScale);
|
||||||
|
float h = PropHelper.fromPercentageToFloat(mH, mHeight, 0, mScale);
|
||||||
float x = PropHelper.fromPercentageToFloat(mX, width, 0, mScale);
|
float rx = PropHelper.fromPercentageToFloat(mRx, mWidth, 0, mScale);
|
||||||
float y = PropHelper.fromPercentageToFloat(mY, height, 0, mScale);
|
float ry = PropHelper.fromPercentageToFloat(mRy, mHeight, 0, mScale);
|
||||||
float w = PropHelper.fromPercentageToFloat(mWidth, width, 0, mScale);
|
|
||||||
float h = PropHelper.fromPercentageToFloat(mHeight, height, 0, mScale);
|
|
||||||
float rx = PropHelper.fromPercentageToFloat(mRx, width, 0, mScale);
|
|
||||||
float ry = PropHelper.fromPercentageToFloat(mRy, height, 0, mScale);
|
|
||||||
|
|
||||||
if (rx != 0 || ry != 0) {
|
if (rx != 0 || ry != 0) {
|
||||||
if (rx == 0) {
|
if (rx == 0) {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Horcrux.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.horcrux.svg;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.facebook.infer.annotation.Assertions;
|
||||||
|
import com.facebook.react.common.SystemClock;
|
||||||
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
|
import com.facebook.react.uimanager.UIManagerModule;
|
||||||
|
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||||
|
import com.facebook.react.uimanager.events.TouchEvent;
|
||||||
|
import com.facebook.react.uimanager.events.TouchEventType;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
// NativeGestureUtil.notifyNativeGestureStarted
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom {@link View} implementation that draws an RNSVGSvg React view and its \children.
|
||||||
|
*/
|
||||||
|
public class RNSVGRenderableView extends ViewGroup {
|
||||||
|
|
||||||
|
public RNSVGRenderableView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,22 +9,32 @@
|
|||||||
|
|
||||||
package com.horcrux.svg;
|
package com.horcrux.svg;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
//import com.facebook.react.uimanager.ReactStylesDiffMap;
|
//import com.facebook.react.uimanager.ReactStylesDiffMap;
|
||||||
|
import com.facebook.react.common.SystemClock;
|
||||||
|
import com.facebook.react.touch.JSResponderHandler;
|
||||||
|
import com.facebook.react.touch.ReactInterceptingViewGroup;
|
||||||
import com.facebook.react.uimanager.ReactShadowNode;
|
import com.facebook.react.uimanager.ReactShadowNode;
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
|
import com.facebook.react.uimanager.UIManagerModule;
|
||||||
|
import com.facebook.react.uimanager.ViewGroupManager;
|
||||||
import com.facebook.react.uimanager.ViewManager;
|
import com.facebook.react.uimanager.ViewManager;
|
||||||
|
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||||
|
import com.facebook.react.uimanager.events.TouchEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewManager for all shadowed RNSVG views: Group, Path and Text. Since these never get rendered
|
* ViewManager for all shadowed RNSVG views: Group, Path and Text. Since these never get rendered
|
||||||
* into native views and don't need any logic (all the logic is in {@link RNSVGSvgView}), this
|
* into native views and don't need any logic (all the logic is in {@link RNSVGSvgView}), this
|
||||||
* "stubbed" ViewManager is used for all of them.
|
* "stubbed" ViewManager is used for all of them.
|
||||||
*/
|
*/
|
||||||
public class RNSVGRenderableViewManager extends ViewManager<View, ReactShadowNode> {
|
public class RNSVGRenderableViewManager extends ViewGroupManager<ViewGroup> {
|
||||||
|
|
||||||
/* package */ static final String CLASS_GROUP = "RNSVGGroup";
|
/* package */ static final String CLASS_GROUP = "RNSVGGroup";
|
||||||
/* package */ static final String CLASS_SVG = "RNSVGPath";
|
/* package */ static final String CLASS_PATH = "RNSVGPath";
|
||||||
/* package */ static final String CLASS_TEXT = "RNSVGText";
|
/* package */ static final String CLASS_TEXT = "RNSVGText";
|
||||||
/* package */ static final String CLASS_IMAGE = "RNSVGImage";
|
/* package */ static final String CLASS_IMAGE = "RNSVGImage";
|
||||||
/* package */ static final String CLASS_CIRCLE = "RNSVGCircle";
|
/* package */ static final String CLASS_CIRCLE = "RNSVGCircle";
|
||||||
@@ -34,12 +44,14 @@ public class RNSVGRenderableViewManager extends ViewManager<View, ReactShadowNod
|
|||||||
|
|
||||||
private final String mClassName;
|
private final String mClassName;
|
||||||
|
|
||||||
|
protected RNSVGVirtualNode mVirtualNode;
|
||||||
|
|
||||||
public static RNSVGRenderableViewManager createRNSVGGroupViewManager() {
|
public static RNSVGRenderableViewManager createRNSVGGroupViewManager() {
|
||||||
return new RNSVGRenderableViewManager(CLASS_GROUP);
|
return new RNSVGRenderableViewManager(CLASS_GROUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RNSVGRenderableViewManager createRNSVGPathViewManager() {
|
public static RNSVGRenderableViewManager createRNSVGPathViewManager() {
|
||||||
return new RNSVGRenderableViewManager(CLASS_SVG);
|
return new RNSVGRenderableViewManager(CLASS_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RNSVGRenderableViewManager createRNSVGTextViewManager() {
|
public static RNSVGRenderableViewManager createRNSVGTextViewManager() {
|
||||||
@@ -76,58 +88,66 @@ public class RNSVGRenderableViewManager extends ViewManager<View, ReactShadowNod
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReactShadowNode createShadowNodeInstance() {
|
public RNSVGVirtualNode createShadowNodeInstance() {
|
||||||
if (mClassName == CLASS_GROUP) {
|
switch (mClassName) {
|
||||||
return new RNSVGGroupShadowNode();
|
case CLASS_GROUP:
|
||||||
} else if (mClassName == CLASS_SVG) {
|
mVirtualNode = new RNSVGGroupShadowNode();
|
||||||
return new RNSVGPathShadowNode();
|
break;
|
||||||
} else if (mClassName == CLASS_CIRCLE) {
|
case CLASS_PATH:
|
||||||
return new RNSVGCircleShadowNode();
|
mVirtualNode = new RNSVGPathShadowNode();
|
||||||
} else if (mClassName == CLASS_ELLIPSE) {
|
break;
|
||||||
return new RNSVGEllipseShadowNode();
|
case CLASS_CIRCLE:
|
||||||
} else if (mClassName == CLASS_LINE) {
|
mVirtualNode = new RNSVGCircleShadowNode();
|
||||||
return new RNSVGLineShadowNode();
|
break;
|
||||||
} else if (mClassName == CLASS_RECT) {
|
case CLASS_ELLIPSE:
|
||||||
return new RNSVGRectShadowNode();
|
mVirtualNode = new RNSVGEllipseShadowNode();
|
||||||
} else if (mClassName == CLASS_TEXT) {
|
break;
|
||||||
return new RNSVGTextShadowNode();
|
case CLASS_LINE:
|
||||||
} else if (mClassName == CLASS_IMAGE) {
|
mVirtualNode = new RNSVGLineShadowNode();
|
||||||
return new RNSVGImageShadowNode();
|
break;
|
||||||
} else {
|
case CLASS_RECT:
|
||||||
throw new IllegalStateException("Unexpected type " + mClassName);
|
mVirtualNode = new RNSVGRectShadowNode();
|
||||||
|
break;
|
||||||
|
case CLASS_TEXT:
|
||||||
|
mVirtualNode = new RNSVGTextShadowNode();
|
||||||
|
break;
|
||||||
|
case CLASS_IMAGE:
|
||||||
|
mVirtualNode = new RNSVGImageShadowNode();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unexpected type " + mClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mVirtualNode;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends RNSVGVirtualNode> getShadowNodeClass() {
|
||||||
|
switch (mClassName) {
|
||||||
|
case CLASS_GROUP:
|
||||||
|
return RNSVGGroupShadowNode.class;
|
||||||
|
case CLASS_PATH:
|
||||||
|
return RNSVGPathShadowNode.class;
|
||||||
|
case CLASS_CIRCLE:
|
||||||
|
return RNSVGCircleShadowNode.class;
|
||||||
|
case CLASS_ELLIPSE:
|
||||||
|
return RNSVGEllipseShadowNode.class;
|
||||||
|
case CLASS_LINE:
|
||||||
|
return RNSVGLineShadowNode.class;
|
||||||
|
case CLASS_RECT:
|
||||||
|
return RNSVGRectShadowNode.class;
|
||||||
|
case CLASS_TEXT:
|
||||||
|
return RNSVGTextShadowNode.class;
|
||||||
|
case CLASS_IMAGE:
|
||||||
|
return RNSVGImageShadowNode.class;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unexpected type " + mClassName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends ReactShadowNode> getShadowNodeClass() {
|
protected ViewGroup createViewInstance(ThemedReactContext reactContext) {
|
||||||
if (mClassName == CLASS_GROUP) {
|
return new RNSVGRenderableView(reactContext);
|
||||||
return RNSVGGroupShadowNode.class;
|
|
||||||
} else if (mClassName == CLASS_SVG) {
|
|
||||||
return RNSVGPathShadowNode.class;
|
|
||||||
} else if (mClassName == CLASS_CIRCLE) {
|
|
||||||
return RNSVGCircleShadowNode.class;
|
|
||||||
} else if (mClassName == CLASS_ELLIPSE) {
|
|
||||||
return RNSVGEllipseShadowNode.class;
|
|
||||||
} else if (mClassName == CLASS_LINE) {
|
|
||||||
return RNSVGLineShadowNode.class;
|
|
||||||
} else if (mClassName == CLASS_RECT) {
|
|
||||||
return RNSVGRectShadowNode.class;
|
|
||||||
} else if (mClassName == CLASS_TEXT) {
|
|
||||||
return RNSVGTextShadowNode.class;
|
|
||||||
} else if (mClassName == CLASS_IMAGE) {
|
|
||||||
return RNSVGImageShadowNode.class;
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Unexpected type " + mClassName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected View createViewInstance(ThemedReactContext reactContext) {
|
|
||||||
throw new IllegalStateException("RNSVGPath does not map into a native view");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateExtraData(View root, Object extraData) {
|
|
||||||
throw new IllegalStateException("RNSVGPath does not map into a native view");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,15 +14,43 @@ import javax.annotation.Nullable;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import com.facebook.infer.annotation.Assertions;
|
||||||
|
import com.facebook.react.common.SystemClock;
|
||||||
|
import com.facebook.react.touch.OnInterceptTouchEventListener;
|
||||||
|
import com.facebook.react.touch.ReactInterceptingViewGroup;
|
||||||
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
|
import com.facebook.react.uimanager.TouchTargetHelper;
|
||||||
|
import com.facebook.react.uimanager.UIManagerModule;
|
||||||
|
import com.facebook.react.uimanager.events.TouchEvent;
|
||||||
|
import com.facebook.react.uimanager.events.TouchEventType;
|
||||||
|
import com.facebook.react.views.view.ReactClippingViewGroup;
|
||||||
|
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||||
|
import com.facebook.react.uimanager.events.NativeGestureUtil;
|
||||||
|
|
||||||
|
// NativeGestureUtil.notifyNativeGestureStarted
|
||||||
/**
|
/**
|
||||||
* Custom {@link View} implementation that draws an RNSVGSvg React view and its children.
|
* Custom {@link View} implementation that draws an RNSVGSvg React view and its \children.
|
||||||
*/
|
*/
|
||||||
public class RNSVGSvgView extends View {
|
public class RNSVGSvgView extends ViewGroup {
|
||||||
|
|
||||||
private @Nullable Bitmap mBitmap;
|
private @Nullable Bitmap mBitmap;
|
||||||
|
|
||||||
|
private RNSVGSvgViewShadowNode mSvgViewShadowNode;
|
||||||
|
|
||||||
|
private int mTargetTag;
|
||||||
|
|
||||||
|
public RNSVGSvgView(Context context, RNSVGSvgViewShadowNode shadowNode) {
|
||||||
|
super(context);
|
||||||
|
mSvgViewShadowNode = shadowNode;
|
||||||
|
}
|
||||||
|
|
||||||
public RNSVGSvgView(Context context) {
|
public RNSVGSvgView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
@@ -42,4 +70,113 @@ public class RNSVGSvgView extends View {
|
|||||||
canvas.drawBitmap(mBitmap, 0, 0, null);
|
canvas.drawBitmap(mBitmap, 0, 0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||||
|
mTargetTag = mSvgViewShadowNode.hitTest(new Point((int) event.getX(), (int) event.getY()), this);
|
||||||
|
if (mTargetTag != -1) {
|
||||||
|
EventDispatcher eventDispatcher = ((ThemedReactContext) this.getContext()).getNativeModule(UIManagerModule.class).getEventDispatcher();
|
||||||
|
handleTouchEvent(event, eventDispatcher);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.dispatchTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) {
|
||||||
|
int action = ev.getAction() & MotionEvent.ACTION_MASK;
|
||||||
|
if (action == MotionEvent.ACTION_DOWN) {
|
||||||
|
eventDispatcher.dispatchEvent(
|
||||||
|
TouchEvent.obtain(
|
||||||
|
mTargetTag,
|
||||||
|
SystemClock.nanoTime(),
|
||||||
|
TouchEventType.START,
|
||||||
|
ev,
|
||||||
|
ev.getX(),
|
||||||
|
ev.getX()));
|
||||||
|
} else if (mTargetTag == -1) {
|
||||||
|
// All the subsequent action types are expected to be called after ACTION_DOWN thus target
|
||||||
|
// is supposed to be set for them.
|
||||||
|
Log.e(
|
||||||
|
"error",
|
||||||
|
"Unexpected state: received touch event but didn't get starting ACTION_DOWN for this " +
|
||||||
|
"gesture before");
|
||||||
|
} else if (action == MotionEvent.ACTION_UP) {
|
||||||
|
// End of the gesture. We reset target tag to -1 and expect no further event associated with
|
||||||
|
// this gesture.
|
||||||
|
eventDispatcher.dispatchEvent(
|
||||||
|
TouchEvent.obtain(
|
||||||
|
mTargetTag,
|
||||||
|
SystemClock.nanoTime(),
|
||||||
|
TouchEventType.END,
|
||||||
|
ev,
|
||||||
|
ev.getX(),
|
||||||
|
ev.getY()));
|
||||||
|
mTargetTag = -1;
|
||||||
|
} else if (action == MotionEvent.ACTION_MOVE) {
|
||||||
|
// Update pointer position for current gesture
|
||||||
|
eventDispatcher.dispatchEvent(
|
||||||
|
TouchEvent.obtain(
|
||||||
|
mTargetTag,
|
||||||
|
SystemClock.nanoTime(),
|
||||||
|
TouchEventType.MOVE,
|
||||||
|
ev,
|
||||||
|
ev.getX(),
|
||||||
|
ev.getY()));
|
||||||
|
} else if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
||||||
|
// New pointer goes down, this can only happen after ACTION_DOWN is sent for the first pointer
|
||||||
|
eventDispatcher.dispatchEvent(
|
||||||
|
TouchEvent.obtain(
|
||||||
|
mTargetTag,
|
||||||
|
SystemClock.nanoTime(),
|
||||||
|
TouchEventType.START,
|
||||||
|
ev,
|
||||||
|
ev.getX(),
|
||||||
|
ev.getY()));
|
||||||
|
} else if (action == MotionEvent.ACTION_POINTER_UP) {
|
||||||
|
// Exactly onw of the pointers goes up
|
||||||
|
eventDispatcher.dispatchEvent(
|
||||||
|
TouchEvent.obtain(
|
||||||
|
mTargetTag,
|
||||||
|
SystemClock.nanoTime(),
|
||||||
|
TouchEventType.END,
|
||||||
|
ev,
|
||||||
|
ev.getX(),
|
||||||
|
ev.getY()));
|
||||||
|
} else if (action == MotionEvent.ACTION_CANCEL) {
|
||||||
|
dispatchCancelEvent(ev, eventDispatcher);
|
||||||
|
mTargetTag = -1;
|
||||||
|
} else {
|
||||||
|
Log.w(
|
||||||
|
"IGNORE",
|
||||||
|
"Warning : touch event was ignored. Action=" + action + " Target=" + mTargetTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchCancelEvent(MotionEvent androidEvent, EventDispatcher eventDispatcher) {
|
||||||
|
// This means the gesture has already ended, via some other CANCEL or UP event. This is not
|
||||||
|
// expected to happen very often as it would mean some child View has decided to intercept the
|
||||||
|
// touch stream and start a native gesture only upon receiving the UP/CANCEL event.
|
||||||
|
if (mTargetTag == -1) {
|
||||||
|
Log.w(
|
||||||
|
"error",
|
||||||
|
"Can't cancel already finished gesture. Is a child View trying to start a gesture from " +
|
||||||
|
"an UP/CANCEL event?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assertions.assertNotNull(eventDispatcher).dispatchEvent(
|
||||||
|
TouchEvent.obtain(
|
||||||
|
mTargetTag,
|
||||||
|
SystemClock.nanoTime(),
|
||||||
|
TouchEventType.CANCEL,
|
||||||
|
androidEvent,
|
||||||
|
androidEvent.getX(),
|
||||||
|
androidEvent.getY()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,27 +10,27 @@
|
|||||||
package com.horcrux.svg;
|
package com.horcrux.svg;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.facebook.csslayout.CSSNode;
|
import com.facebook.csslayout.CSSNode;
|
||||||
import com.facebook.csslayout.MeasureOutput;
|
import com.facebook.csslayout.MeasureOutput;
|
||||||
import com.facebook.react.uimanager.BaseViewManager;
|
import com.facebook.react.uimanager.BaseViewManager;
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
|
import com.facebook.react.uimanager.ViewGroupManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewManager for RNSVGSvgView React views. Renders as a {@link RNSVGSvgView} and handles
|
* ViewManager for RNSVGSvgView React views. Renders as a {@link RNSVGSvgView} and handles
|
||||||
* invalidating the native view on shadow view updates happening in the underlying tree.
|
* invalidating the native view on shadow view updates happening in the underlying tree.
|
||||||
*/
|
*/
|
||||||
public class RNSVGSvgViewManager extends
|
public class RNSVGSvgViewManager extends ViewGroupManager<RNSVGSvgView> {
|
||||||
BaseViewManager<RNSVGSvgView, RNSVGSvgViewShadowNode> {
|
|
||||||
|
|
||||||
private static final String REACT_CLASS = "RNSVGSvgView";
|
private static final String REACT_CLASS = "RNSVGSvgView";
|
||||||
|
|
||||||
private static final CSSNode.MeasureFunction MEASURE_FUNCTION = new CSSNode.MeasureFunction() {
|
// TODO: use an ArrayList to connect RNSVGSvgViewShadowNode with RNSVGSvgView, not sure if there will be a race condition.
|
||||||
@Override
|
// TODO: find a better way to replace this
|
||||||
public void measure(CSSNode node, float width, float height, MeasureOutput measureOutput) {
|
private ArrayList<RNSVGSvgViewShadowNode> SvgShadowNodes = new ArrayList<>();
|
||||||
throw new IllegalStateException("SvgView should have explicit width and height set");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@@ -40,7 +40,7 @@ public class RNSVGSvgViewManager extends
|
|||||||
@Override
|
@Override
|
||||||
public RNSVGSvgViewShadowNode createShadowNodeInstance() {
|
public RNSVGSvgViewShadowNode createShadowNodeInstance() {
|
||||||
RNSVGSvgViewShadowNode node = new RNSVGSvgViewShadowNode();
|
RNSVGSvgViewShadowNode node = new RNSVGSvgViewShadowNode();
|
||||||
node.setMeasureFunction(MEASURE_FUNCTION);
|
SvgShadowNodes.add(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,9 @@ public class RNSVGSvgViewManager extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RNSVGSvgView createViewInstance(ThemedReactContext reactContext) {
|
protected RNSVGSvgView createViewInstance(ThemedReactContext reactContext) {
|
||||||
return new RNSVGSvgView(reactContext);
|
RNSVGSvgViewShadowNode shadowNode = SvgShadowNodes.get(0);
|
||||||
|
SvgShadowNodes.remove(0);
|
||||||
|
return new RNSVGSvgView(reactContext, shadowNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ package com.horcrux.svg;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||||
import com.facebook.react.uimanager.UIViewOperationQueue;
|
import com.facebook.react.uimanager.UIViewOperationQueue;
|
||||||
@@ -20,17 +23,6 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
|||||||
* Shadow node for RNSVG virtual tree root - RNSVGSvgView
|
* Shadow node for RNSVG virtual tree root - RNSVGSvgView
|
||||||
*/
|
*/
|
||||||
public class RNSVGSvgViewShadowNode extends LayoutShadowNode {
|
public class RNSVGSvgViewShadowNode extends LayoutShadowNode {
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVirtual() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVirtualAnchor() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) {
|
public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) {
|
||||||
super.onCollectExtraUpdates(uiUpdater);
|
super.onCollectExtraUpdates(uiUpdater);
|
||||||
@@ -40,17 +32,35 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode {
|
|||||||
private Object drawOutput() {
|
private Object drawOutput() {
|
||||||
// TODO(7255985): Use TextureView and pass Svg from the view to draw on it asynchronously
|
// TODO(7255985): Use TextureView and pass Svg from the view to draw on it asynchronously
|
||||||
// instead of passing the bitmap (which is inefficient especially in terms of memory usage)
|
// instead of passing the bitmap (which is inefficient especially in terms of memory usage)
|
||||||
|
float width = (int) getLayoutWidth();
|
||||||
|
float height = (int) getLayoutHeight();
|
||||||
Bitmap bitmap = Bitmap.createBitmap(
|
Bitmap bitmap = Bitmap.createBitmap(
|
||||||
(int) getLayoutWidth(),
|
(int) width,
|
||||||
(int) getLayoutHeight(),
|
(int) height,
|
||||||
Bitmap.Config.ARGB_8888);
|
Bitmap.Config.ARGB_8888);
|
||||||
Canvas canvas = new Canvas(bitmap);
|
Canvas canvas = new Canvas(bitmap);
|
||||||
Paint paint = new Paint();
|
Paint paint = new Paint();
|
||||||
|
|
||||||
for (int i = 0; i < getChildCount(); i++) {
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i);
|
RNSVGVirtualNode child = (RNSVGVirtualNode) getChildAt(i);
|
||||||
|
child.setDimensions(width, height);
|
||||||
child.draw(canvas, paint, 1f);
|
child.draw(canvas, paint, 1f);
|
||||||
child.markUpdateSeen();
|
child.markUpdateSeen();
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int hitTest(Point point, ViewGroup view) {
|
||||||
|
int count = getChildCount();
|
||||||
|
int viewTag = -1;
|
||||||
|
for (int i = count - 1; i >= 0; i--) {
|
||||||
|
viewTag = ((RNSVGVirtualNode) getChildAt(i)).hitTest(point, view.getChildAt(i));
|
||||||
|
if (viewTag != -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewTag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,18 @@ package com.horcrux.svg;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
@@ -84,26 +88,26 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
|
|||||||
RectF box = getBox(paint, text);
|
RectF box = getBox(paint, text);
|
||||||
|
|
||||||
if (setupStrokePaint(paint, opacity, box)) {
|
if (setupStrokePaint(paint, opacity, box)) {
|
||||||
applyTextPropertiesToPaint(paint);
|
drawText(canvas, paint, text);
|
||||||
if (mPath == null) {
|
|
||||||
canvas.drawText(text, 0, -paint.ascent(), paint);
|
|
||||||
} else {
|
|
||||||
canvas.drawTextOnPath(text, mPath, 0, -paint.ascent(), paint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (setupFillPaint(paint, opacity, box)) {
|
if (setupFillPaint(paint, opacity, box)) {
|
||||||
applyTextPropertiesToPaint(paint);
|
drawText(canvas, paint, text);
|
||||||
|
|
||||||
if (mPath == null) {
|
|
||||||
canvas.drawText(text, 0, -paint.ascent(), paint);
|
|
||||||
} else {
|
|
||||||
canvas.drawTextOnPath(text, mPath, 0, -paint.ascent(), paint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreCanvas(canvas);
|
restoreCanvas(canvas);
|
||||||
markUpdateSeen();
|
markUpdateSeen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void drawText(Canvas canvas, Paint paint, String text) {
|
||||||
|
applyTextPropertiesToPaint(paint);
|
||||||
|
|
||||||
|
if (mPath == null) {
|
||||||
|
canvas.drawText(text, 0, -paint.ascent(), paint);
|
||||||
|
} else {
|
||||||
|
canvas.drawTextOnPath(text, mPath, 0, -paint.ascent(), paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String formatText() {
|
private String formatText() {
|
||||||
if (mFrame == null || !mFrame.hasKey(PROP_LINES)) {
|
if (mFrame == null || !mFrame.hasKey(PROP_LINES)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -170,6 +174,8 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
protected Path getPath(Canvas canvas, Paint paint) {
|
protected Path getPath(Canvas canvas, Paint paint) {
|
||||||
Path path = new Path();
|
Path path = new Path();
|
||||||
|
|
||||||
@@ -178,6 +184,7 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: get path while TextPath is set.
|
||||||
if (setupFillPaint(paint, 1.0f, getBox(paint, text))) {
|
if (setupFillPaint(paint, 1.0f, getBox(paint, text))) {
|
||||||
applyTextPropertiesToPaint(paint);
|
applyTextPropertiesToPaint(paint);
|
||||||
paint.getTextPath(text, 0, text.length(), 0, -paint.ascent(), path);
|
paint.getTextPath(text, 0, text.length(), 0, -paint.ascent(), path);
|
||||||
@@ -186,4 +193,43 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
|
|||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hitTest(Point point, View view) {
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(
|
||||||
|
(int) mWidth,
|
||||||
|
(int) mHeight,
|
||||||
|
Bitmap.Config.ARGB_8888);
|
||||||
|
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
canvas.concat(mMatrix);
|
||||||
|
|
||||||
|
// todo clip detect
|
||||||
|
|
||||||
|
String text = formatText();
|
||||||
|
if (text == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Paint paint = new Paint();
|
||||||
|
clip(canvas, paint);
|
||||||
|
setHitTestFill(paint);
|
||||||
|
drawText(canvas, paint, text);
|
||||||
|
|
||||||
|
if (setHitTestStroke(paint)) {
|
||||||
|
drawText(canvas, paint, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.setBitmap(bitmap);
|
||||||
|
try {
|
||||||
|
if (bitmap.getPixel(point.x, point.y) != 0) {
|
||||||
|
return view.getId();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return -1;
|
||||||
|
} finally {
|
||||||
|
bitmap.recycle();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,19 +16,21 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.graphics.Region;
|
import android.graphics.Region;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||||
|
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||||
import com.facebook.react.uimanager.ReactShadowNode;
|
import com.facebook.react.uimanager.ReactShadowNode;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -36,18 +38,19 @@ import java.util.Map;
|
|||||||
* Base class for RNSVGView virtual nodes: {@link RNSVGGroupShadowNode}, {@link RNSVGPathShadowNode} and
|
* Base class for RNSVGView virtual nodes: {@link RNSVGGroupShadowNode}, {@link RNSVGPathShadowNode} and
|
||||||
* indirectly for {@link RNSVGTextShadowNode}.
|
* indirectly for {@link RNSVGTextShadowNode}.
|
||||||
*/
|
*/
|
||||||
public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
public abstract class RNSVGVirtualNode extends LayoutShadowNode {
|
||||||
protected static Map<String, Path> CLIP_PATHS = new HashMap<>();
|
protected static Map<String, Path> CLIP_PATHS = new HashMap<>();
|
||||||
|
|
||||||
protected static final float MIN_OPACITY_FOR_DRAW = 0.01f;
|
protected static final float MIN_OPACITY_FOR_DRAW = 0.01f;
|
||||||
|
|
||||||
private static final float[] sMatrixData = new float[9];
|
private static final float[] sMatrixData = new float[9];
|
||||||
private static final float[] sRawMatrix = new float[9];
|
private static final float[] sRawMatrix = new float[9];
|
||||||
private @Nullable String mDefinedClipPathId;
|
|
||||||
protected float mOpacity = 1f;
|
protected float mOpacity = 1f;
|
||||||
protected @Nullable Matrix mMatrix = new Matrix();
|
protected @Nullable Matrix mMatrix = new Matrix();
|
||||||
|
|
||||||
|
private @Nullable String mDefinedClipPathId;
|
||||||
protected @Nullable Path mClipPath;
|
protected @Nullable Path mClipPath;
|
||||||
private @Nullable String mClipPathId;
|
protected @Nullable String mClipPathId;
|
||||||
private static final int PATH_TYPE_ARC = 4;
|
private static final int PATH_TYPE_ARC = 4;
|
||||||
private static final int PATH_TYPE_CLOSE = 1;
|
private static final int PATH_TYPE_CLOSE = 1;
|
||||||
private static final int PATH_TYPE_CURVETO = 3;
|
private static final int PATH_TYPE_CURVETO = 3;
|
||||||
@@ -62,15 +65,13 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
|||||||
private boolean mClipRuleSet;
|
private boolean mClipRuleSet;
|
||||||
private boolean mClipDataSet;
|
private boolean mClipDataSet;
|
||||||
|
|
||||||
|
protected float mWidth = 0;
|
||||||
|
protected float mHeight = 0;
|
||||||
|
|
||||||
public RNSVGVirtualNode() {
|
public RNSVGVirtualNode() {
|
||||||
mScale = DisplayMetricsHolder.getWindowDisplayMetrics().density;
|
mScale = DisplayMetricsHolder.getWindowDisplayMetrics().density;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVirtual() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void draw(Canvas canvas, Paint paint, float opacity);
|
public abstract void draw(Canvas canvas, Paint paint, float opacity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,8 +128,8 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
|||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactProp(name = "transform")
|
@ReactProp(name = "trans")
|
||||||
public void setTransform(@Nullable ReadableArray transformArray) {
|
public void setTrans(@Nullable ReadableArray transformArray) {
|
||||||
if (transformArray != null) {
|
if (transformArray != null) {
|
||||||
int matrixSize = PropHelper.toFloatArray(transformArray, sMatrixData);
|
int matrixSize = PropHelper.toFloatArray(transformArray, sMatrixData);
|
||||||
if (matrixSize == 6) {
|
if (matrixSize == 6) {
|
||||||
@@ -155,7 +156,6 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
|||||||
throw new JSApplicationIllegalArgumentException(
|
throw new JSApplicationIllegalArgumentException(
|
||||||
"clipRule " + mClipRule + " unrecognized");
|
"clipRule " + mClipRule + " unrecognized");
|
||||||
}
|
}
|
||||||
|
|
||||||
createPath(mClipData, mClipPath);
|
createPath(mClipData, mClipPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,7 +204,6 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (i < data.length) {
|
while (i < data.length) {
|
||||||
|
|
||||||
int type = (int) data[i++];
|
int type = (int) data[i++];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PATH_TYPE_MOVETO:
|
case PATH_TYPE_MOVETO:
|
||||||
@@ -270,16 +269,23 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDimensions(float width, float height) {
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public int hitTest(Point point, View view);
|
||||||
|
|
||||||
protected void defineClipPath(Path clipPath, String clipPathId) {
|
protected void defineClipPath(Path clipPath, String clipPathId) {
|
||||||
CLIP_PATHS.put(clipPathId, clipPath);
|
CLIP_PATHS.put(clipPathId, clipPath);
|
||||||
mDefinedClipPathId = clipPathId;
|
mDefinedClipPathId = clipPathId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract protected Path getPath(Canvas canvas, Paint paint);
|
||||||
|
|
||||||
protected void finalize() {
|
protected void finalize() {
|
||||||
if (mDefinedClipPathId != null) {
|
if (mDefinedClipPathId != null) {
|
||||||
CLIP_PATHS.remove(mDefinedClipPathId);
|
CLIP_PATHS.remove(mDefinedClipPathId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected Path getPath(Canvas canvas, Paint paint);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import React, {Children, Component, cloneElement, PropTypes} from 'react';
|
import React, {Children, Component, cloneElement, PropTypes} from 'react';
|
||||||
import {View, requireNativeComponent} from 'react-native';
|
import {View, requireNativeComponent, StyleSheet} from 'react-native';
|
||||||
import ViewBox from './ViewBox';
|
import ViewBox from './ViewBox';
|
||||||
|
|
||||||
// Svg - Root node of all Svg elements
|
// Svg - Root node of all Svg elements
|
||||||
let id = 0;
|
let id = 0;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
svg: {
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
class Svg extends Component{
|
class Svg extends Component{
|
||||||
static displayName = 'Svg';
|
static displayName = 'Svg';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -75,6 +81,7 @@ class Svg extends Component{
|
|||||||
preserveAspectRatio={null}
|
preserveAspectRatio={null}
|
||||||
ref={ele => this.root = ele}
|
ref={ele => this.root = ele}
|
||||||
style={[
|
style={[
|
||||||
|
styles.svg,
|
||||||
props.style,
|
props.style,
|
||||||
!isNaN(opacity) && {
|
!isNaN(opacity) && {
|
||||||
opacity
|
opacity
|
||||||
|
|||||||
Reference in New Issue
Block a user