decouple textview from fresco

Reviewed By: andreicoman11

Differential Revision: D2960626

fb-gh-sync-id: c03aa7f16cdea795cefe39da2c5d660ae6278a37
shipit-source-id: c03aa7f16cdea795cefe39da2c5d660ae6278a37
This commit is contained in:
Felix Oghina 2016-02-26 06:15:58 -08:00 committed by Facebook Github Bot 8
parent c32e5fd84f
commit 9baef48498
12 changed files with 396 additions and 292 deletions

View File

@ -19,6 +19,7 @@ android_library(
react_native_target('java/com/facebook/react/views/swiperefresh:swiperefresh'),
react_native_target('java/com/facebook/react/views/switchview:switchview'),
react_native_target('java/com/facebook/react/views/text:text'),
react_native_target('java/com/facebook/react/views/text/frescosupport:frescosupport'),
react_native_target('java/com/facebook/react/views/textinput:textinput'),
react_native_target('java/com/facebook/react/views/toolbar:toolbar'),
react_native_target('java/com/facebook/react/views/view:view'),

View File

@ -22,8 +22,8 @@ import com.facebook.react.modules.camera.CameraRollManager;
import com.facebook.react.modules.camera.ImageEditingManager;
import com.facebook.react.modules.camera.ImageStoreManager;
import com.facebook.react.modules.clipboard.ClipboardModule;
import com.facebook.react.modules.dialog.DialogModule;
import com.facebook.react.modules.datepicker.DatePickerDialogModule;
import com.facebook.react.modules.dialog.DialogModule;
import com.facebook.react.modules.fresco.FrescoModule;
import com.facebook.react.modules.intent.IntentModule;
import com.facebook.react.modules.location.LocationModule;
@ -45,16 +45,16 @@ import com.facebook.react.views.progressbar.ReactProgressBarViewManager;
import com.facebook.react.views.recyclerview.RecyclerViewBackedScrollViewManager;
import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager;
import com.facebook.react.views.scroll.ReactScrollViewManager;
import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager;
import com.facebook.react.views.switchview.ReactSwitchManager;
import com.facebook.react.views.text.ReactRawTextManager;
import com.facebook.react.views.text.ReactTextViewManager;
import com.facebook.react.views.text.ReactTextInlineImageViewManager;
import com.facebook.react.views.text.ReactVirtualTextViewManager;
import com.facebook.react.views.textfrescosupport.FrescoBasedReactTextInlineImageViewManager;
import com.facebook.react.views.textinput.ReactTextInputManager;
import com.facebook.react.views.toolbar.ReactToolbarManager;
import com.facebook.react.views.view.ReactViewManager;
import com.facebook.react.views.viewpager.ReactViewPagerManager;
import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager;
import com.facebook.react.views.webview.ReactWebViewManager;
/**
@ -106,7 +106,7 @@ public class MainReactPackage implements ReactPackage {
new ReactRawTextManager(),
new ReactScrollViewManager(),
new ReactSwitchManager(),
new ReactTextInlineImageViewManager(),
new FrescoBasedReactTextInlineImageViewManager(),
new ReactTextInputManager(),
new ReactTextViewManager(),
new ReactToolbarManager(),

View File

@ -9,10 +9,6 @@ android_library(
react_native_target('java/com/facebook/csslayout:csslayout'),
react_native_target('java/com/facebook/react/uimanager:uimanager'),
react_native_target('java/com/facebook/react/uimanager/annotations:annotations'),
react_native_dep('libraries/fresco/fresco-react-native:fbcore'),
react_native_dep('libraries/fresco/fresco-react-native:fresco-react-native'),
react_native_dep('libraries/fresco/fresco-react-native:fresco-drawee'),
react_native_dep('libraries/fresco/fresco-react-native:imagepipeline'),
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
react_native_dep('third-party/java/jsr-305:jsr-305'),
],

View File

@ -10,7 +10,6 @@
package com.facebook.react.views.text;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ThemedReactContext;
/**

View File

@ -9,90 +9,17 @@
package com.facebook.react.views.text;
import javax.annotation.Nullable;
import java.util.Locale;
import android.content.Context;
import android.net.Uri;
import com.facebook.common.util.UriUtil;
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.ReactShadowNode;
/**
* {@link ReactShadowNode} class for Image embedded within a TextView.
*
* Base class for {@link com.facebook.csslayout.CSSNode}s that represent inline images.
*/
public class ReactTextInlineImageShadowNode extends LayoutShadowNode {
public abstract class ReactTextInlineImageShadowNode extends LayoutShadowNode {
private @Nullable Uri mUri;
private final AbstractDraweeControllerBuilder mDraweeControllerBuilder;
private final @Nullable Object mCallerContext;
public ReactTextInlineImageShadowNode(
AbstractDraweeControllerBuilder draweeControllerBuilder,
@Nullable Object callerContext) {
mDraweeControllerBuilder = draweeControllerBuilder;
mCallerContext = callerContext;
}
@ReactProp(name = "src")
public void setSource(@Nullable String source) {
Uri uri = null;
if (source != null) {
try {
uri = Uri.parse(source);
// Verify scheme is set, so that relative uri (used by static resources) are not handled.
if (uri.getScheme() == null) {
uri = null;
}
} catch (Exception e) {
// ignore malformed uri, then attempt to extract resource ID.
}
if (uri == null) {
uri = getResourceDrawableUri(getThemedContext(), source);
}
}
if (uri != mUri) {
markUpdated();
}
mUri = uri;
}
public @Nullable Uri getUri() {
return mUri;
}
// TODO: t9053573 is tracking that this code should be shared
private static @Nullable Uri getResourceDrawableUri(Context context, @Nullable String name) {
if (name == null || name.isEmpty()) {
return null;
}
name = name.toLowerCase(Locale.getDefault()).replace("-", "_");
int resId = context.getResources().getIdentifier(
name,
"drawable",
context.getPackageName());
return new Uri.Builder()
.scheme(UriUtil.LOCAL_RESOURCE_SCHEME)
.path(String.valueOf(resId))
.build();
}
@Override
public boolean isVirtual() {
return true;
}
public AbstractDraweeControllerBuilder getDraweeControllerBuilder() {
return mDraweeControllerBuilder;
}
public @Nullable Object getCallerContext() {
return mCallerContext;
}
/**
* Build a {@link TextInlineImageSpan} from this node. This will be added to the TextView in
* place of this node.
*/
public abstract TextInlineImageSpan buildInlineImageSpan();
}

View File

@ -14,7 +14,6 @@ import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.text.BoringLayout;
import android.text.Layout;
@ -37,11 +36,11 @@ import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.ViewDefaults;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.annotations.ReactProp;
/**
* {@link ReactShadowNode} class for spannable text view.
@ -94,7 +93,7 @@ public class ReactTextShadowNode extends LayoutShadowNode {
}
}
private static final void buildSpannedFromTextCSSNode(
private static void buildSpannedFromTextCSSNode(
ReactTextShadowNode textCSSNode,
SpannableStringBuilder sb,
List<SetSpanOperation> ops) {
@ -107,7 +106,14 @@ public class ReactTextShadowNode extends LayoutShadowNode {
if (child instanceof ReactTextShadowNode) {
buildSpannedFromTextCSSNode((ReactTextShadowNode) child, sb, ops);
} else if (child instanceof ReactTextInlineImageShadowNode) {
buildSpannedFromImageNode((ReactTextInlineImageShadowNode) child, sb, ops);
// We make the image take up 1 character in the span and put a corresponding character into
// the text so that the image doesn't run over any following text.
sb.append(INLINE_IMAGE_PLACEHOLDER);
ops.add(
new SetSpanOperation(
sb.length() - INLINE_IMAGE_PLACEHOLDER.length(),
sb.length(),
((ReactTextInlineImageShadowNode) child).buildInlineImageSpan()));
} else {
throw new IllegalViewOperationException("Unexpected view type nested under text node: "
+ child.getClass());
@ -154,36 +160,14 @@ public class ReactTextShadowNode extends LayoutShadowNode {
}
}
private static final void buildSpannedFromImageNode(
ReactTextInlineImageShadowNode node,
SpannableStringBuilder sb,
List<SetSpanOperation> ops) {
int start = sb.length();
// Create our own internal ImageSpan which will allow us to correctly layout the Image
Resources resources = node.getThemedContext().getResources();
int height = (int) Math.ceil(node.getStyleHeight());
int width = (int) Math.ceil(node.getStyleWidth());
TextInlineImageSpan imageSpan = new TextInlineImageSpan(
resources,
height,
width,
node.getUri(),
node.getDraweeControllerBuilder(),
node.getCallerContext());
// We make the image take up 1 character in the span and put a corresponding character into the
// text so that the image doesn't run over any following text.
sb.append(INLINE_IMAGE_PLACEHOLDER);
ops.add(new SetSpanOperation(start, sb.length(), imageSpan));
}
protected static final Spannable fromTextCSSNode(ReactTextShadowNode textCSSNode) {
protected static Spannable fromTextCSSNode(ReactTextShadowNode textCSSNode) {
SpannableStringBuilder sb = new SpannableStringBuilder();
// TODO(5837930): Investigate whether it's worth optimizing this part and do it if so
// The {@link SpannableStringBuilder} implementation require setSpan operation to be called
// up-to-bottom, otherwise all the spannables that are withing the region for which one may set
// a new spannable will be wiped out
List<SetSpanOperation> ops = new ArrayList<SetSpanOperation>();
List<SetSpanOperation> ops = new ArrayList<>();
buildSpannedFromTextCSSNode(textCSSNode, sb, ops);
if (textCSSNode.mFontSize == UNSET) {
sb.setSpan(
@ -330,6 +314,13 @@ public class ReactTextShadowNode extends LayoutShadowNode {
protected boolean mContainsImages = false;
public ReactTextShadowNode(boolean isVirtual) {
mIsVirtual = isVirtual;
if (!isVirtual) {
setMeasureFunction(TEXT_MEASURE_FUNCTION);
}
}
@Override
public void onBeforeLayout() {
if (mIsVirtual) {
@ -483,11 +474,4 @@ public class ReactTextShadowNode extends LayoutShadowNode {
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate);
}
}
public ReactTextShadowNode(boolean isVirtual) {
mIsVirtual = isVirtual;
if (!isVirtual) {
setMeasureFunction(TEXT_MEASURE_FUNCTION);
}
}
}

View File

@ -7,163 +7,64 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.text;
package com.facebook.react.views.text;
import javax.annotation.Nullable;
import javax.annotation.Nullable;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.Spannable;
import android.text.style.ReplacementSpan;
import android.widget.TextView;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.style.ReplacementSpan;
import android.view.View;
import android.widget.TextView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.DraweeHolder;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
/**
* Base class for inline image spans.
*/
public abstract class TextInlineImageSpan extends ReplacementSpan {
/**
* TextInlineImageSpan is a span for Images that are inside <Text/>. It computes it's size based
* on the input size. When it is time to draw, it will use the Fresco framework to get the right
* Drawable and let that draw.
*
* Since Fresco needs to callback to the TextView that contains this, in the ViewManager, you must
* tell the Span about the TextView
*
* Note: It borrows code from DynamicDrawableSpan and if that code updates how it computes size or
* draws, we need to update this as well.
*/
public class TextInlineImageSpan extends ReplacementSpan {
/**
* For TextInlineImageSpan we need to update the Span to know that the window is attached and
* the TextView that we will set as the callback on the Drawable.
*
* @param spannable The spannable that may contain TextInlineImageSpans
* @param view The view which will be set as the callback for the Drawable
*/
public static void possiblyUpdateInlineImageSpans(Spannable spannable, TextView view) {
TextInlineImageSpan[] spans =
spannable.getSpans(0, spannable.length(), TextInlineImageSpan.class);
for (TextInlineImageSpan span : spans) {
span.onAttachedToWindow();
span.setTextView(view);
}
}
private @Nullable Drawable mDrawable;
private final AbstractDraweeControllerBuilder mDraweeControllerBuilder;
private final DraweeHolder<GenericDraweeHierarchy> mDraweeHolder;
private final @Nullable Object mCallerContext;
/**
* Get the drawable that is span represents.
*/
public abstract @Nullable Drawable getDrawable();
private int mHeight;
private Uri mUri;
private int mWidth;
/**
* Called by the text view from {@link View#onDetachedFromWindow()},
*/
public abstract void onDetachedFromWindow();
private @Nullable TextView mTextView;
/**
* Called by the text view from {@link View#onStartTemporaryDetach()}.
*/
public abstract void onStartTemporaryDetach();
public TextInlineImageSpan(
Resources resources,
int height,
int width,
@Nullable Uri uri,
AbstractDraweeControllerBuilder draweeControllerBuilder,
@Nullable Object callerContext) {
mDraweeHolder = new DraweeHolder(
GenericDraweeHierarchyBuilder.newInstance(resources)
.build()
);
mDraweeControllerBuilder = draweeControllerBuilder;
mCallerContext = callerContext;
/**
* Called by the text view from {@link View#onAttachedToWindow()}.
*/
public abstract void onAttachedToWindow();
mHeight = height;
mWidth = width;
mUri = (uri != null) ? uri : Uri.EMPTY;
}
/**
* Called by the text view from {@link View#onFinishTemporaryDetach()}.
*/
public abstract void onFinishTemporaryDetach();
/**
* The ReactTextView that holds this ImageSpan is responsible for passing these methods on so
* that we can do proper lifetime management for Fresco
*/
public void onDetachedFromWindow() {
mDraweeHolder.onDetach();
}
public void onStartTemporaryDetach() {
mDraweeHolder.onDetach();
}
public void onAttachedToWindow() {
mDraweeHolder.onAttach();
}
public void onFinishTemporaryDetach() {
mDraweeHolder.onAttach();
}
public @Nullable Drawable getDrawable() {
return mDrawable;
}
@Override
public int getSize(
Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
// NOTE: This getSize code is copied from DynamicDrawableSpan and modified to not use a Drawable
if (fm != null) {
fm.ascent = -mHeight;
fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
}
return mWidth;
}
@Override
public void draw(
Canvas canvas,
CharSequence text,
int start,
int end,
float x,
int top,
int y,
int bottom,
Paint paint) {
if (mDrawable == null) {
ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(mUri)
.build();
DraweeController draweeController = mDraweeControllerBuilder
.reset()
.setOldController(mDraweeHolder.getController())
.setCallerContext(mCallerContext)
.setImageRequest(imageRequest)
.build();
mDraweeHolder.setController(draweeController);
mDrawable = mDraweeHolder.getTopLevelDrawable();
mDrawable.setBounds(0, 0, mWidth, mHeight);
mDrawable.setCallback(mTextView);
}
// NOTE: This drawing code is copied from DynamicDrawableSpan
canvas.save();
int transY = bottom - mDrawable.getBounds().bottom;
canvas.translate(x, transY);
mDrawable.draw(canvas);
canvas.restore();
}
/**
* For TextInlineImageSpan we need to update the Span to know that the window is attached and
* the TextView that we will set as the callback on the Drawable.
*
* @param spannable The spannable that may contain TextInlineImageSpans
* @param view The view which will be set as the callback for the Drawable
*/
public static void possiblyUpdateInlineImageSpans(Spannable spannable, TextView view) {
TextInlineImageSpan[] spans =
spannable.getSpans(0, spannable.length(), TextInlineImageSpan.class);
for (TextInlineImageSpan span : spans) {
span.onAttachedToWindow();
span.mTextView = view;
}
};
}
/**
* Set the textview that will contain this span.
*/
public abstract void setTextView(TextView textView);
}

View File

@ -0,0 +1,27 @@
include_defs('//ReactAndroid/DEFS')
android_library(
name = 'frescosupport',
srcs = glob(['*.java']),
deps = [
react_native_target('java/com/facebook/csslayout:csslayout'),
react_native_target('java/com/facebook/react/bridge:bridge'),
react_native_target('java/com/facebook/react/common:common'),
react_native_target('java/com/facebook/react/uimanager:uimanager'),
react_native_target('java/com/facebook/react/uimanager/annotations:annotations'),
react_native_target('java/com/facebook/react/views/text:text'),
react_native_dep('libraries/fresco/fresco-react-native:fbcore'),
react_native_dep('libraries/fresco/fresco-react-native:fresco-drawee'),
react_native_dep('libraries/fresco/fresco-react-native:fresco-react-native'),
react_native_dep('libraries/fresco/fresco-react-native:imagepipeline'),
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
react_native_dep('third-party/java/jsr-305:jsr-305'),
],
visibility = [
'PUBLIC',
],
)
project_config(
src_target = ':frescosupport',
)

View File

@ -0,0 +1,114 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.textfrescosupport;
import javax.annotation.Nullable;
import java.util.Locale;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import com.facebook.common.util.UriUtil;
import com.facebook.csslayout.CSSNode;
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.text.ReactTextInlineImageShadowNode;
import com.facebook.react.views.text.TextInlineImageSpan;
/**
* {@link CSSNode} that represents an inline image. Loading is done using Fresco.
*
*/
public class FrescoBasedReactTextInlineImageShadowNode extends ReactTextInlineImageShadowNode {
private @Nullable Uri mUri;
private final AbstractDraweeControllerBuilder mDraweeControllerBuilder;
private final @Nullable Object mCallerContext;
public FrescoBasedReactTextInlineImageShadowNode(
AbstractDraweeControllerBuilder draweeControllerBuilder,
@Nullable Object callerContext) {
mDraweeControllerBuilder = draweeControllerBuilder;
mCallerContext = callerContext;
}
@ReactProp(name = "src")
public void setSource(@Nullable String source) {
Uri uri = null;
if (source != null) {
try {
uri = Uri.parse(source);
// Verify scheme is set, so that relative uri (used by static resources) are not handled.
if (uri.getScheme() == null) {
uri = null;
}
} catch (Exception e) {
// ignore malformed uri, then attempt to extract resource ID.
}
if (uri == null) {
uri = getResourceDrawableUri(getThemedContext(), source);
}
}
if (uri != mUri) {
markUpdated();
}
mUri = uri;
}
public @Nullable Uri getUri() {
return mUri;
}
// TODO: t9053573 is tracking that this code should be shared
private static @Nullable Uri getResourceDrawableUri(Context context, @Nullable String name) {
if (name == null || name.isEmpty()) {
return null;
}
name = name.toLowerCase(Locale.getDefault()).replace("-", "_");
int resId = context.getResources().getIdentifier(
name,
"drawable",
context.getPackageName());
return new Uri.Builder()
.scheme(UriUtil.LOCAL_RESOURCE_SCHEME)
.path(String.valueOf(resId))
.build();
}
@Override
public boolean isVirtual() {
return true;
}
@Override
public TextInlineImageSpan buildInlineImageSpan() {
Resources resources = getThemedContext().getResources();
int height = (int) Math.ceil(getStyleHeight());
int width = (int) Math.ceil(getStyleWidth());
return new FrescoBasedReactTextInlineImageSpan(
resources,
height,
width,
getUri(),
getDraweeControllerBuilder(),
getCallerContext());
}
public AbstractDraweeControllerBuilder getDraweeControllerBuilder() {
return mDraweeControllerBuilder;
}
public @Nullable Object getCallerContext() {
return mCallerContext;
}
}

View File

@ -0,0 +1,155 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.textfrescosupport;
import javax.annotation.Nullable;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.widget.TextView;
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.DraweeHolder;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.facebook.react.views.text.TextInlineImageSpan;
/**
* FrescoBasedTextInlineImageSpan is a span for Images that are inside <Text/>. It computes
* its size based on the input size. When it is time to draw, it will use the Fresco framework to
* get the right Drawable and let that draw.
*
* Since Fresco needs to callback to the TextView that contains this, in the ViewManager, you must
* tell the Span about the TextView
*
* Note: It borrows code from DynamicDrawableSpan and if that code updates how it computes size or
* draws, we need to update this as well.
*/
public class FrescoBasedReactTextInlineImageSpan extends TextInlineImageSpan {
private @Nullable Drawable mDrawable;
private final AbstractDraweeControllerBuilder mDraweeControllerBuilder;
private final DraweeHolder<GenericDraweeHierarchy> mDraweeHolder;
private final @Nullable Object mCallerContext;
private int mHeight;
private Uri mUri;
private int mWidth;
private @Nullable TextView mTextView;
public FrescoBasedReactTextInlineImageSpan(
Resources resources,
int height,
int width,
@Nullable Uri uri,
AbstractDraweeControllerBuilder draweeControllerBuilder,
@Nullable Object callerContext) {
mDraweeHolder = new DraweeHolder(
GenericDraweeHierarchyBuilder.newInstance(resources)
.build()
);
mDraweeControllerBuilder = draweeControllerBuilder;
mCallerContext = callerContext;
mHeight = height;
mWidth = width;
mUri = (uri != null) ? uri : Uri.EMPTY;
}
/**
* The ReactTextView that holds this ImageSpan is responsible for passing these methods on so
* that we can do proper lifetime management for Fresco
*/
public void onDetachedFromWindow() {
mDraweeHolder.onDetach();
}
public void onStartTemporaryDetach() {
mDraweeHolder.onDetach();
}
public void onAttachedToWindow() {
mDraweeHolder.onAttach();
}
public void onFinishTemporaryDetach() {
mDraweeHolder.onAttach();
}
public @Nullable Drawable getDrawable() {
return mDrawable;
}
@Override
public int getSize(
Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
// NOTE: This getSize code is copied from DynamicDrawableSpan and modified to not use a Drawable
if (fm != null) {
fm.ascent = -mHeight;
fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
}
return mWidth;
}
public void setTextView(TextView textView) {
mTextView = textView;
}
@Override
public void draw(
Canvas canvas,
CharSequence text,
int start,
int end,
float x,
int top,
int y,
int bottom,
Paint paint) {
if (mDrawable == null) {
ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(mUri)
.build();
DraweeController draweeController = mDraweeControllerBuilder
.reset()
.setOldController(mDraweeHolder.getController())
.setCallerContext(mCallerContext)
.setImageRequest(imageRequest)
.build();
mDraweeHolder.setController(draweeController);
mDrawable = mDraweeHolder.getTopLevelDrawable();
mDrawable.setBounds(0, 0, mWidth, mHeight);
mDrawable.setCallback(mTextView);
}
// NOTE: This drawing code is copied from DynamicDrawableSpan
canvas.save();
int transY = bottom - mDrawable.getBounds().bottom;
canvas.translate(x, transY);
mDrawable.draw(canvas);
canvas.restore();
}
}

View File

@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.text;
package com.facebook.react.views.textfrescosupport;
import javax.annotation.Nullable;
@ -19,24 +19,24 @@ import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManager;
/**
* Manages Images embedded in Text nodes. Since they are used only as a virtual nodes any type of
* native view operation will throw an {@link IllegalStateException}
* Manages Images embedded in Text nodes using Fresco. Since they are used only as a virtual nodes
* any type of native view operation will throw an {@link IllegalStateException}.
*/
public class ReactTextInlineImageViewManager
extends ViewManager<View, ReactTextInlineImageShadowNode> {
public class FrescoBasedReactTextInlineImageViewManager
extends ViewManager<View, FrescoBasedReactTextInlineImageShadowNode> {
static final String REACT_CLASS = "RCTTextInlineImage";
private final @Nullable AbstractDraweeControllerBuilder mDraweeControllerBuilder;
private final @Nullable Object mCallerContext;
public ReactTextInlineImageViewManager() {
public FrescoBasedReactTextInlineImageViewManager() {
this(null, null);
}
public ReactTextInlineImageViewManager(
@Nullable AbstractDraweeControllerBuilder draweeControllerBuilder,
@Nullable Object callerContext) {
public FrescoBasedReactTextInlineImageViewManager(
@Nullable AbstractDraweeControllerBuilder draweeControllerBuilder,
@Nullable Object callerContext) {
mDraweeControllerBuilder = draweeControllerBuilder;
mCallerContext = callerContext;
}
@ -52,18 +52,18 @@ public class ReactTextInlineImageViewManager
}
@Override
public ReactTextInlineImageShadowNode createShadowNodeInstance() {
return new ReactTextInlineImageShadowNode(
(mDraweeControllerBuilder != null) ?
mDraweeControllerBuilder :
Fresco.newDraweeControllerBuilder(),
mCallerContext
public FrescoBasedReactTextInlineImageShadowNode createShadowNodeInstance() {
return new FrescoBasedReactTextInlineImageShadowNode(
(mDraweeControllerBuilder != null) ?
mDraweeControllerBuilder :
Fresco.newDraweeControllerBuilder(),
mCallerContext
);
}
@Override
public Class<ReactTextInlineImageShadowNode> getShadowNodeClass() {
return ReactTextInlineImageShadowNode.class;
public Class<FrescoBasedReactTextInlineImageShadowNode> getShadowNodeClass() {
return FrescoBasedReactTextInlineImageShadowNode.class;
}
@Override

View File

@ -43,8 +43,8 @@ import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.views.text.DefaultStyleValuesUtil;
import com.facebook.react.views.text.ReactTextUpdate;
import com.facebook.react.views.text.TextInlineImageSpan;
import com.facebook.react.views.text.ReactTextUpdate;
/**
* Manages instances of TextInput.