Merge pull request #94 from silverspace/android_blank_initial_render

Android: SVG Images not rendering the first time a view is displayed
This commit is contained in:
Horcrux
2016-07-30 10:37:37 +08:00
committed by GitHub
5 changed files with 67 additions and 28 deletions
@@ -16,12 +16,10 @@ import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.net.Uri;
import android.util.Log;
import com.facebook.common.executors.UiThreadImmediateExecutorService;
import com.facebook.common.logging.FLog;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.common.logging.FLog;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableBitmap;
@@ -31,6 +29,9 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -44,7 +45,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
private String mW;
private String mH;
private Uri mUri;
private boolean mLoading;
private AtomicBoolean mLoading = new AtomicBoolean(false);
@ReactProp(name = "x")
public void setX(String x) {
@@ -86,13 +87,14 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
@Override
public void draw(final Canvas canvas, final Paint paint, final float opacity) {
final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(mUri).build();
final boolean inMemoryCache = Fresco.getImagePipeline().isInBitmapMemoryCache(request);
if (!mLoading.get()) {
final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(mUri).build();
if (inMemoryCache) {
tryRender(request, canvas, paint, opacity * mOpacity);
} else if (!mLoading) {
loadBitmap(request, canvas, paint);
if (Fresco.getImagePipeline().isInBitmapMemoryCache(request)) {
tryRender(request, canvas, paint, opacity * mOpacity);
} else {
loadBitmap(request, canvas, paint);
}
}
}
@@ -106,8 +108,10 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
if (bitmap != null) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
paint.reset();
mLoading = false;
mLoading.set(false);
getSvgShadowNode().drawChildren(canvas, paint);
getSvgShadowNode().invalidateView(getRect());
}
}
@@ -115,7 +119,7 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
public void onFailureImpl(DataSource dataSource) {
// No cleanup required here.
// TODO: more details about this failure
mLoading = false;
mLoading.set(false);
FLog.w(ReactConstants.TAG, dataSource.getFailureCause(), "RNSVG: fetchDecodedImage failed!");
}
},
@@ -123,18 +127,25 @@ public class RNSVGImageShadowNode extends RNSVGPathShadowNode {
);
}
private void doRender(@Nonnull final Canvas canvas, @Nonnull final Paint paint, @Nonnull final Bitmap bitmap, final float opacity) {
final int count = saveAndSetupCanvas(canvas);
clip(canvas, paint);
@Nonnull
private Rect getRect() {
float x = PropHelper.fromPercentageToFloat(mX, mCanvasWidth, 0, mScale);
float y = PropHelper.fromPercentageToFloat(mY, mCanvasHeight, 0, mScale);
float w = PropHelper.fromPercentageToFloat(mW, mCanvasWidth, 0, mScale);
float h = PropHelper.fromPercentageToFloat(mH, mCanvasHeight, 0, mScale);
return new Rect((int) x, (int) y, (int) (x + w), (int) (y + h));
}
private void doRender(@Nonnull final Canvas canvas, @Nonnull final Paint paint, @Nonnull final Bitmap bitmap, final float opacity) {
final int count = saveAndSetupCanvas(canvas);
clip(canvas, paint);
Paint alphaPaint = new Paint();
alphaPaint.setAlpha((int) (opacity * 255));
canvas.drawBitmap(bitmap, null, new Rect((int) x, (int) y, (int) (x + w), (int) (y + h)), alphaPaint);
canvas.drawBitmap(bitmap, null, getRect(), alphaPaint);
restoreCanvas(canvas, count);
markUpdateSeen();
@@ -310,7 +310,9 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
mPath.computeBounds(box, true);
}
PropHelper.RNSVGBrush brush = getSvgShadowNode().getDefinedBrush(colors.getString(1));
brush.setupPaint(paint, box, mScale, opacity);
if (brush != null) {
brush.setupPaint(paint, box, mScale, opacity);
}
} else {
// TODO: Support pattern.
FLog.w(ReactConstants.TAG, "RNSVG: Color type " + colorType + " not supported!");
@@ -10,13 +10,14 @@
package com.horcrux.svg;
import android.graphics.Bitmap;
import android.util.Log;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import java.util.ArrayList;
import javax.annotation.Nullable;
/**
* 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.
@@ -24,6 +25,7 @@ import java.util.ArrayList;
public class RNSVGSvgViewManager extends ViewGroupManager<RNSVGSvgView> {
private static final String REACT_CLASS = "RNSVGSvgView";
private RNSVGSvgView svgView = null;
// TODO: use an ArrayList to connect RNSVGSvgViewShadowNode with RNSVGSvgView, not sure if there will be a race condition.
// TODO: find a better way to replace this
@@ -34,9 +36,14 @@ public class RNSVGSvgViewManager extends ViewGroupManager<RNSVGSvgView> {
return REACT_CLASS;
}
@Nullable
public RNSVGSvgView getSvgView() {
return this.svgView;
}
@Override
public RNSVGSvgViewShadowNode createShadowNodeInstance() {
RNSVGSvgViewShadowNode node = new RNSVGSvgViewShadowNode();
RNSVGSvgViewShadowNode node = new RNSVGSvgViewShadowNode(this);
SvgShadowNodes.add(node);
return node;
}
@@ -50,7 +57,8 @@ public class RNSVGSvgViewManager extends ViewGroupManager<RNSVGSvgView> {
protected RNSVGSvgView createViewInstance(ThemedReactContext reactContext) {
RNSVGSvgViewShadowNode shadowNode = SvgShadowNodes.get(0);
SvgShadowNodes.remove(0);
return new RNSVGSvgView(reactContext, shadowNode);
this.svgView = new RNSVGSvgView(reactContext, shadowNode);
return this.svgView;
}
@Override
@@ -13,15 +13,19 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.Log;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.UIViewOperationQueue;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
/**
* Shadow node for RNSVG virtual tree root - RNSVGSvgView
*/
@@ -32,6 +36,13 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode {
private static final Map<String, RNSVGVirtualNode> mDefinedTemplates = new HashMap<>();
private static final Map<String, PropHelper.RNSVGBrush> mDefinedBrushes = new HashMap<>();
@Nonnull private final RNSVGSvgViewManager viewManager;
public RNSVGSvgViewShadowNode(@Nonnull final RNSVGSvgViewManager viewManager) {
super();
this.viewManager = viewManager;
}
@Override
public void onCollectExtraUpdates(UIViewOperationQueue uiUpdater) {
super.onCollectExtraUpdates(uiUpdater);
@@ -54,7 +65,7 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode {
* Draw all of the child nodes of this root node
*
* This method is synchronized since
* {@link com.horcrux.svg.RNSVGImageShadowNode#loadImage(ImageRequest, Canvas, Paint)} calls it
* {@link com.horcrux.svg.RNSVGImageShadowNode#loadBitmap(ImageRequest, Canvas, Paint)} calls it
* asynchronously after images have loaded and are ready to be drawn.
*
* @param canvas
@@ -77,6 +88,16 @@ public class RNSVGSvgViewShadowNode extends LayoutShadowNode {
}
}
protected void invalidateView(@Nonnull final Rect dirtyRect) {
final RNSVGSvgView svgView = this.viewManager.getSvgView();
if (svgView != null) {
final View rootView = svgView.getRootView();
if (rootView != null) {
rootView.invalidate(dirtyRect);
}
}
}
public void enableTouchEvents() {
if (!mResponsible) {
mResponsible = true;
@@ -9,8 +9,6 @@
package com.horcrux.svg;
import javax.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -18,16 +16,16 @@ import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.Log;
import android.view.View;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
/**
* Base class for RNSVGView virtual nodes: {@link RNSVGGroupShadowNode}, {@link RNSVGPathShadowNode} and
@@ -81,8 +79,7 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
* @param canvas the canvas to set up
*/
protected final int saveAndSetupCanvas(Canvas canvas) {
int count = canvas.getSaveCount();
canvas.save();
final int count = canvas.save();
if (mMatrix != null) {
canvas.concat(mMatrix);
}