From 36badf821b4d0f4cbde45c85e5d18ab5bc588d72 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Tue, 11 Feb 2020 15:19:58 +0100 Subject: [PATCH] fix(android): starting android label rewrite Use native code as much as possible for better performances --- .gitignore | 1 + plugin/platforms/android/include.gradle | 9 + .../label/CustomTypefaceSpan.java | 45 ++ .../java/com/nativescript/label/Font.java | 240 +++++++++++ .../java/com/nativescript/label/Label.java | 64 +++ src/label.android.ts | 393 +++++++++--------- src/typings/android.d.ts | 11 + 7 files changed, 567 insertions(+), 196 deletions(-) create mode 100644 plugin/platforms/android/include.gradle create mode 100644 plugin/platforms/android/java/com/nativescript/label/CustomTypefaceSpan.java create mode 100644 plugin/platforms/android/java/com/nativescript/label/Font.java create mode 100644 plugin/platforms/android/java/com/nativescript/label/Label.java create mode 100644 src/typings/android.d.ts diff --git a/.gitignore b/.gitignore index 03f1540..e2fd033 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ plugin/*.d.ts /plugin/typings /plugin/README.md /pnpm-lock.yaml +*.aar diff --git a/plugin/platforms/android/include.gradle b/plugin/platforms/android/include.gradle new file mode 100644 index 0000000..d024618 --- /dev/null +++ b/plugin/platforms/android/include.gradle @@ -0,0 +1,9 @@ + + + +dependencies { + def androidxVersion = project.hasProperty("androidxVersion") ? project.androidxVersion : "1.1.0" + + compile "androidx.core:core:$androidxVersion" + compile 'com.lsjwzh.widget:FastTextView:1.2.20' +} \ No newline at end of file diff --git a/plugin/platforms/android/java/com/nativescript/label/CustomTypefaceSpan.java b/plugin/platforms/android/java/com/nativescript/label/CustomTypefaceSpan.java new file mode 100644 index 0000000..3c474e8 --- /dev/null +++ b/plugin/platforms/android/java/com/nativescript/label/CustomTypefaceSpan.java @@ -0,0 +1,45 @@ +package com.nativescript.label; + +import android.annotation.SuppressLint; +import android.graphics.Typeface; +import android.text.TextPaint; +import android.text.style.TypefaceSpan; + +/** + * Created by hhristov on 2/27/17. + */ + +@SuppressLint("ParcelCreator") +public class CustomTypefaceSpan extends TypefaceSpan { + private Typeface typeface; + + public CustomTypefaceSpan(String family, Typeface typeface) { + super(family); + this.typeface = typeface; + } + + public void updateDrawState(TextPaint ds) { + this.applyCustomTypeFace(ds); + } + + public void updateMeasureState(TextPaint paint) { + this.applyCustomTypeFace(paint); + } + + private void applyCustomTypeFace(TextPaint paint) { + final Typeface old = paint.getTypeface(); + final int oldStyle = (old == null) ? 0 : old.getStyle(); + + Typeface typeface = this.typeface; + int fake = oldStyle & ~typeface.getStyle(); + if ((fake & android.graphics.Typeface.BOLD) != 0) { + paint.setFakeBoldText(true); + } + + if ((fake & android.graphics.Typeface.ITALIC) != 0) { + paint.setTextSkewX(-0.25f); + } + + paint.setTypeface(typeface); + } +} diff --git a/plugin/platforms/android/java/com/nativescript/label/Font.java b/plugin/platforms/android/java/com/nativescript/label/Font.java new file mode 100644 index 0000000..df54653 --- /dev/null +++ b/plugin/platforms/android/java/com/nativescript/label/Font.java @@ -0,0 +1,240 @@ +package com.nativescript.label; + +import android.content.Context; +import android.content.res.AssetManager; +import android.graphics.Typeface; +import android.os.Build; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.TypefaceSpan; +import android.util.Log; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; + +import androidx.core.text.HtmlCompat; + +public class Font { + static AssetManager appAssets; + static HashMap typefaceCache = new HashMap(); + + static final String TAG = "Font"; + + public static Typeface loadFontFromFile(Context context, String fontFolder, String fontFamily) { + if (typefaceCache.containsKey(fontFamily)) { + return typefaceCache.get(fontFamily); + } + if (fontFamily.startsWith("res/")) { + int fontID = context.getResources().getIdentifier(fontFamily.substring(4), "font", + context.getPackageName()); + Typeface result = androidx.core.content.res.ResourcesCompat.getFont(context, fontID); + if (result != null) { + typefaceCache.put(fontFamily, result); + } + return result; + } + + if (appAssets == null) { + appAssets = context.getAssets(); + } + if (appAssets == null) { + return null; + } + + Typeface result = typefaceCache.get(fontFamily); + // Check for undefined explicitly as null mean we tried to load the font, but + // failed. + File file = new File(fontFolder, fontFamily + ".ttf"); + // const basePath = fs.path.join(fs.knownFolders.currentApp().path, "fonts", + // fontFamily); + + if (!file.exists()) { + file = new File(fontFolder, fontFamily + ".otf"); + if (!file.exists()) { + Log.w(TAG, "Could not find font file for " + fontFamily); + return null; + } + + } + + try { + result = Typeface.createFromFile(file.getAbsolutePath()); + } catch (Exception e) { + Log.w(TAG, "\"Error loading font asset: " + file.getAbsolutePath() + "," + e.getLocalizedMessage()); + } + typefaceCache.put(fontFamily, result); + + return result; + } + + public interface FontWeight { + String THIN = "100"; + String EXTRA_LIGHT = "200"; + String LIGHT = "300"; + String NORMAL = "normal"; + String MEDIUM = "500"; + String SEMI_BOLD = "600"; + String BOLD = "bold"; + String EXTRA_BOLD = "800"; + String BLACK = "900"; + } + + public interface genericFontFamilies { + String serif = "serif"; + String sansSerif = "sans-serif"; + String monospace = "monospace"; + String system = "system"; + } + + public static String getFontWeightSuffix(String fontWeight) { + if (fontWeight == null) { + return ""; + } + switch (fontWeight) { + case FontWeight.THIN: + return Build.VERSION.SDK_INT >= 16 ? "-thin" : ""; + case FontWeight.EXTRA_LIGHT: + case FontWeight.LIGHT: + return Build.VERSION.SDK_INT >= 16 ? "-light" : ""; + case FontWeight.NORMAL: + case "400": + return ""; + case FontWeight.MEDIUM: + case FontWeight.SEMI_BOLD: + return Build.VERSION.SDK_INT >= 21 ? "-medium" : ""; + case FontWeight.BOLD: + case "700": + case FontWeight.EXTRA_BOLD: + return ""; + case FontWeight.BLACK: + return Build.VERSION.SDK_INT >= 21 ? "-black" : ""; + default: + throw new Error("Invalid font weight:" + fontWeight); + } + } + + public static ArrayList parseFontFamily(String value) { + ArrayList result = new ArrayList(); + if (value == null) { + return result; + } + + String[] split = value.split(","); + for (int i = 0; i < split.length; i++) { + String str = split[i].trim().replaceAll("['\"]+", ""); + if (str != null) { + result.add(str); + } + } + + return result; + } + + public static Typeface createTypeface(Context context, String fontFolder, String fontFamily, String fontWeight, + boolean isBold, boolean isItalic) { + int fontStyle = 0; + if (isBold) { + fontStyle |= Typeface.BOLD; + } + if (isItalic) { + fontStyle |= Typeface.ITALIC; + } + + // http://stackoverflow.com/questions/19691530/valid-values-for-androidfontfamily-and-what-they-map-to + ArrayList fonts = parseFontFamily(fontFamily); + Typeface result = null; + for (int i = 0; i < fonts.size(); i++) { + switch (fonts.get(i).toLowerCase()) { + case genericFontFamilies.serif: + result = Typeface.create("serif" + getFontWeightSuffix(fontWeight), fontStyle); + break; + + case genericFontFamilies.sansSerif: + case genericFontFamilies.system: + result = Typeface.create("sans-serif" + getFontWeightSuffix(fontWeight), fontStyle); + break; + + case genericFontFamilies.monospace: + result = Typeface.create("monospace" + getFontWeightSuffix(fontWeight), fontStyle); + break; + + default: + result = loadFontFromFile(context, fontFolder, fonts.get(i)); + if (result != null && fontStyle != 0) { + result = Typeface.create(result, fontStyle); + } + break; + } + + if (result != null) { + // Found the font! + break; + } + } + + if (result == null) { + result = Typeface.create("sans-serif" + getFontWeightSuffix(fontWeight), fontStyle); + } + + return result; + } + + public static SpannableStringBuilder stringBuilderFromHtmlString(Context context, String fontFolder, + String htmlString) { + if (htmlString == null) { + return null; + } + Spanned spannedString = HtmlCompat.fromHtml(htmlString, HtmlCompat.FROM_HTML_MODE_COMPACT); + SpannableStringBuilder builder = new SpannableStringBuilder(spannedString); + + TypefaceSpan[] spans = builder.getSpans(0, builder.length(), android.text.style.TypefaceSpan.class); + for (int index = 0; index < spans.length; index++) { + TypefaceSpan span = spans[index]; + int start = builder.getSpanStart(span); + int end = builder.getSpanEnd(span); + String fontFamily = span.getFamily(); + String[] split = fontFamily.split("-"); + String style = null; + if (split.length > 1) { + style = split[1]; + } + // String style = fontFamily.split("-")[1] || builder.removeSpan(span); + // const + // font = new Font(fontFamily, 0, style == = 'italic' ? 'italic' : 'normal', + // style == = 'bold' ? 'bold' : 'normal'); + Typeface typeface = createTypeface(context, fontFolder, fontFamily, style == "bold" ? "bold" : "normal", + style == "bold", style == "italic"); + + if (typeface == null) { + typeface = Typeface.create(fontFamily, Typeface.NORMAL); + } + if (typeface != null) { + TypefaceSpan typefaceSpan = new CustomTypefaceSpan(fontFamily, typeface); + builder.setSpan(typefaceSpan, start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + // const ssb = new android.text.SpannableStringBuilder(); + // for (let i = 0, spanStart = 0, spanLength = 0, length = + // formattedString.spans.length; i < length; i++) { + // const span = formattedString.spans.getItem(i); + // const text = span.text; + // const textTransform = (formattedString.parent).textTransform; + // let spanText = (text === null || text === undefined) ? "" : text.toString(); + // if (textTransform && textTransform !== "none") { + // spanText = getTransformedText(spanText, textTransform); + // } + + // spanLength = spanText.length; + // if (spanLength > 0) { + // ssb.insert(spanStart, spanText); + // setSpanModifiers(ssb, span, spanStart, spanStart + spanLength); + // spanStart += spanLength; + // } + // } + + return builder; + } +} diff --git a/plugin/platforms/android/java/com/nativescript/label/Label.java b/plugin/platforms/android/java/com/nativescript/label/Label.java new file mode 100644 index 0000000..5d8c2a2 --- /dev/null +++ b/plugin/platforms/android/java/com/nativescript/label/Label.java @@ -0,0 +1,64 @@ +package com.nativescript.label; + +import android.graphics.Typeface; +import android.content.Context; +import android.text.method.TransformationMethod; + +public class Label extends com.lsjwzh.widget.text.FastTextView { + public Label(Context context) { + super(context); + } + + public TransformationMethod getTransformationMethod() { + return null; + } + + public void setTransformationMethod(TransformationMethod value) { + + } + public void setSingleLine(boolean value) { + + } + public void setMinLines(int value) { + + } + public void setMinHeight(int value) { + + } + public void setMaxHeight(int value) { + + } + + public int getMinHeight() { + return 0; + + } + public int getMinLines() { + return 0; + + } + public int getMaxHeight() { + return 0; + + } + public int getLineSpacingExtra() { + return 0; + + } + public void setLineSpacingExtra(int value) { + + } + + public Typeface getTypeface() { + return getTextPaint().getTypeface(); + } + public void setTypeface(Typeface typeface) { + getTextPaint().setTypeface(typeface); + } + public int getPaintFlags() { + return getTextPaint().getFlags(); + } + public void setPaintFlags(int typeface) { + getTextPaint().setFlags(typeface); + } +} \ No newline at end of file diff --git a/src/label.android.ts b/src/label.android.ts index fe74ba1..e76c0fd 100644 --- a/src/label.android.ts +++ b/src/label.android.ts @@ -10,9 +10,12 @@ import { profile } from 'tns-core-modules/profiling'; import { TextShadow } from './label'; import { htmlProperty, LabelBase, lineBreakProperty, maxLinesProperty, textShadowProperty, VerticalTextAlignment, verticalTextAlignmentProperty } from './label-common'; +const context = application.android.context; +const fontPath = fs.path.join(fs.knownFolders.currentApp().path, 'fonts'); + Font.prototype.getAndroidTypeface = function() { if (!this._typeface) { - this._typeface = createTypeface(this); + this._typeface = (com as any).nativescript.label.Font.createTypeface(context, fontPath, this.fontFamily, this.fontWeight, this.isBold, this.isItalic); } return this._typeface; }; @@ -20,154 +23,154 @@ Font.prototype.getAndroidTypeface = function() { export * from './label-common'; -let _useAndroidX; -function useAndroidX() { - if (_useAndroidX === undefined) { - _useAndroidX = !!(global as any).androidx && !!(global as any).androidx.appcompat; - } - return _useAndroidX; -} -let _HtmlCompat: typeof androidx.core.text.HtmlCompat; -function HtmlCompat() { - if (_HtmlCompat === undefined) { - _HtmlCompat = useAndroidX() ? (global as any).androidx.core.text.HtmlCompat : android.text.Html; - } - return _HtmlCompat; -} -let _ContentPackageName: typeof androidx.core.content; -function ContentPackageName() { - if (_ContentPackageName === undefined) { - _ContentPackageName = useAndroidX() ? (global as any).androidx.core.content : (android as any).support.v4.content; - } - return _ContentPackageName; -} -let appAssets: android.content.res.AssetManager; -const typefaceCache = new Map(); -const FONTS_BASE_PATH = '/fonts/'; -function loadFontFromFile(fontFamily: string): android.graphics.Typeface { - if (fontFamily.startsWith('res/')) { - let result = typefaceCache.get(fontFamily); - if (!result) { - const context = application.android.context; - const fontID = context.getResources().getIdentifier(fontFamily.slice(4), 'font', context.getPackageName()); - result = ContentPackageName().res.ResourcesCompat.getFont(context, fontID); - if (result) { - typefaceCache.set(fontFamily, result); - } - return result; - } - } - appAssets = appAssets || application.android.context.getAssets(); - if (!appAssets) { - return null; - } - - let result = typefaceCache.get(fontFamily); - // Check for undefined explicitly as null mean we tried to load the font, but failed. - if (result === undefined) { - result = null; - - let fontAssetPath: string; - const basePath = fs.path.join(fs.knownFolders.currentApp().path, 'fonts', fontFamily); - if (fs.File.exists(basePath + '.ttf')) { - fontAssetPath = FONTS_BASE_PATH + fontFamily + '.ttf'; - } else if (fs.File.exists(basePath + '.otf')) { - fontAssetPath = FONTS_BASE_PATH + fontFamily + '.otf'; - } else { - if (traceEnabled()) { - traceWrite('Could not find font file for ' + fontFamily, traceCategories.Error, traceMessageType.error); - } - } - - if (fontAssetPath) { - try { - fontAssetPath = fs.path.join(fs.knownFolders.currentApp().path, fontAssetPath); - result = android.graphics.Typeface.createFromFile(fontAssetPath); - } catch (e) { - if (traceEnabled()) { - traceWrite('Error loading font asset: ' + fontAssetPath, traceCategories.Error, traceMessageType.error); - } - } - } - typefaceCache.set(fontFamily, result); - } - - return result; -} +// let _useAndroidX; +// function useAndroidX() { +// if (_useAndroidX === undefined) { +// _useAndroidX = !!(global as any).androidx && !!(global as any).androidx.appcompat; +// } +// return _useAndroidX; +// } +// let _HtmlCompat: typeof androidx.core.text.HtmlCompat; +// function HtmlCompat() { +// if (_HtmlCompat === undefined) { +// _HtmlCompat = useAndroidX() ? (global as any).androidx.core.text.HtmlCompat : android.text.Html; +// } +// return _HtmlCompat; +// } +// let _ContentPackageName: typeof androidx.core.content; +// function ContentPackageName() { +// if (_ContentPackageName === undefined) { +// _ContentPackageName = useAndroidX() ? (global as any).androidx.core.content : (android as any).support.v4.content; +// } +// return _ContentPackageName; +// } +// let appAssets: android.content.res.AssetManager; +// const typefaceCache = new Map(); +// const FONTS_BASE_PATH = '/fonts/'; +// function loadFontFromFile(fontFamily: string): android.graphics.Typeface { +// if (fontFamily.startsWith('res/')) { +// let result = typefaceCache.get(fontFamily); +// if (!result) { +// const context = application.android.context; +// const fontID = context.getResources().getIdentifier(fontFamily.slice(4), 'font', context.getPackageName()); +// result = ContentPackageName().res.ResourcesCompat.getFont(context, fontID); +// if (result) { +// typefaceCache.set(fontFamily, result); +// } +// return result; +// } +// } +// appAssets = appAssets || application.android.context.getAssets(); +// if (!appAssets) { +// return null; +// } -function createTypeface(font: Font): android.graphics.Typeface { - let fontStyle = 0; - if (font.isBold) { - fontStyle |= android.graphics.Typeface.BOLD; - } - if (font.isItalic) { - fontStyle |= android.graphics.Typeface.ITALIC; - } +// let result = typefaceCache.get(fontFamily); +// // Check for undefined explicitly as null mean we tried to load the font, but failed. +// if (result === undefined) { +// result = null; + +// let fontAssetPath: string; +// const basePath = fs.path.join(fs.knownFolders.currentApp().path, 'fonts', fontFamily); +// if (fs.File.exists(basePath + '.ttf')) { +// fontAssetPath = FONTS_BASE_PATH + fontFamily + '.ttf'; +// } else if (fs.File.exists(basePath + '.otf')) { +// fontAssetPath = FONTS_BASE_PATH + fontFamily + '.otf'; +// } else { +// if (traceEnabled()) { +// traceWrite('Could not find font file for ' + fontFamily, traceCategories.Error, traceMessageType.error); +// } +// } - // http://stackoverflow.com/questions/19691530/valid-values-for-androidfontfamily-and-what-they-map-to - const fonts = parseFontFamily(font.fontFamily); - let result = null; - for (let i = 0; i < fonts.length; i++) { - switch (fonts[i].toLowerCase()) { - case genericFontFamilies.serif: - result = android.graphics.Typeface.create('serif' + getFontWeightSuffix(font.fontWeight), fontStyle); - break; +// if (fontAssetPath) { +// try { +// fontAssetPath = fs.path.join(fs.knownFolders.currentApp().path, fontAssetPath); +// result = android.graphics.Typeface.createFromFile(fontAssetPath); +// } catch (e) { +// if (traceEnabled()) { +// traceWrite('Error loading font asset: ' + fontAssetPath, traceCategories.Error, traceMessageType.error); +// } +// } +// } +// typefaceCache.set(fontFamily, result); +// } - case genericFontFamilies.sansSerif: - case genericFontFamilies.system: - result = android.graphics.Typeface.create('sans-serif' + getFontWeightSuffix(font.fontWeight), fontStyle); - break; +// return result; +// } - case genericFontFamilies.monospace: - result = android.graphics.Typeface.create('monospace' + getFontWeightSuffix(font.fontWeight), fontStyle); - break; +// function createTypeface(font: Font): android.graphics.Typeface { +// let fontStyle = 0; +// if (font.isBold) { +// fontStyle |= android.graphics.Typeface.BOLD; +// } +// if (font.isItalic) { +// fontStyle |= android.graphics.Typeface.ITALIC; +// } - default: - result = loadFontFromFile(fonts[i]); - if (result && fontStyle) { - result = android.graphics.Typeface.create(result, fontStyle); - } - break; - } +// // http://stackoverflow.com/questions/19691530/valid-values-for-androidfontfamily-and-what-they-map-to +// const fonts = parseFontFamily(font.fontFamily); +// let result = null; +// for (let i = 0; i < fonts.length; i++) { +// switch (fonts[i].toLowerCase()) { +// case genericFontFamilies.serif: +// result = android.graphics.Typeface.create('serif' + getFontWeightSuffix(font.fontWeight), fontStyle); +// break; + +// case genericFontFamilies.sansSerif: +// case genericFontFamilies.system: +// result = android.graphics.Typeface.create('sans-serif' + getFontWeightSuffix(font.fontWeight), fontStyle); +// break; + +// case genericFontFamilies.monospace: +// result = android.graphics.Typeface.create('monospace' + getFontWeightSuffix(font.fontWeight), fontStyle); +// break; + +// default: +// result = loadFontFromFile(fonts[i]); +// if (result && fontStyle) { +// result = android.graphics.Typeface.create(result, fontStyle); +// } +// break; +// } - if (result) { - // Found the font! - break; - } - } +// if (result) { +// // Found the font! +// break; +// } +// } - if (!result) { - result = android.graphics.Typeface.create('sans-serif' + getFontWeightSuffix(font.fontWeight), fontStyle); - } +// if (!result) { +// result = android.graphics.Typeface.create('sans-serif' + getFontWeightSuffix(font.fontWeight), fontStyle); +// } - return result; -} +// return result; +// } -function getFontWeightSuffix(fontWeight: FontWeight): string { - switch (fontWeight) { - case FontWeight.THIN: - return android.os.Build.VERSION.SDK_INT >= 16 ? '-thin' : ''; - case FontWeight.EXTRA_LIGHT: - case FontWeight.LIGHT: - return android.os.Build.VERSION.SDK_INT >= 16 ? '-light' : ''; - case FontWeight.NORMAL: - case '400': - case undefined: - case null: - return ''; - case FontWeight.MEDIUM: - case FontWeight.SEMI_BOLD: - return android.os.Build.VERSION.SDK_INT >= 21 ? '-medium' : ''; - case FontWeight.BOLD: - case '700': - case FontWeight.EXTRA_BOLD: - return ''; - case FontWeight.BLACK: - return android.os.Build.VERSION.SDK_INT >= 21 ? '-black' : ''; - default: - throw new Error(`Invalid font weight: "${fontWeight}"`); - } -} +// function getFontWeightSuffix(fontWeight: FontWeight): string { +// switch (fontWeight) { +// case FontWeight.THIN: +// return android.os.Build.VERSION.SDK_INT >= 16 ? '-thin' : ''; +// case FontWeight.EXTRA_LIGHT: +// case FontWeight.LIGHT: +// return android.os.Build.VERSION.SDK_INT >= 16 ? '-light' : ''; +// case FontWeight.NORMAL: +// case '400': +// case undefined: +// case null: +// return ''; +// case FontWeight.MEDIUM: +// case FontWeight.SEMI_BOLD: +// return android.os.Build.VERSION.SDK_INT >= 21 ? '-medium' : ''; +// case FontWeight.BOLD: +// case '700': +// case FontWeight.EXTRA_BOLD: +// return ''; +// case FontWeight.BLACK: +// return android.os.Build.VERSION.SDK_INT >= 21 ? '-black' : ''; +// default: +// throw new Error(`Invalid font weight: "${fontWeight}"`); +// } +// } let TextView: typeof android.widget.TextView; @@ -177,6 +180,7 @@ export class Label extends LabelBase { @profile public createNativeView() { if (!TextView) { + // TextView = (com as any).nativescript.label.Label; TextView = android.widget.TextView; } return new TextView(this._context); @@ -187,14 +191,14 @@ export class Label extends LabelBase { const nativeView = this.nativeViewProtected; // This makes the html work - nativeView.setLinksClickable(false); - nativeView.setMovementMethod(null); + // nativeView.setLinksClickable(false); + // nativeView.setMovementMethod(null); // nativeView.setMovementMethod(android.text.method.LinkMovementMethod.getInstance()); } public resetNativeView(): void { super.resetNativeView(); - this.nativeViewProtected.setAutoLinkMask(0); + // this.nativeViewProtected.setAutoLinkMask(0); } [htmlProperty.getDefault](): string { @@ -202,28 +206,25 @@ export class Label extends LabelBase { } @profile - [formattedTextProperty.setNative](value: FormattedString) { + setFormattedString(value: FormattedString) { super[formattedTextProperty.setNative](value); } + + [formattedTextProperty.setNative](value: FormattedString) { + this.setFormattedString(value); + } + @profile - [htmlProperty.setNative](value: string) { - // If the data.newValue actually has a in it; we need to disable autolink mask - // it internally disables the coloring, but then the links won't work.. So to support both - // styles of links (html and just text based) we have to manually enable/disable the autolink mask - let mask = 15; - if (value.search(/= 0) { - mask = 0; - } + setHtml(value: string) { const nativeView = this.nativeViewProtected; - nativeView.setAutoLinkMask(mask); - let spannableStringBuilder: android.text.SpannableStringBuilder; - if (useAndroidX()) { - spannableStringBuilder = createSpannableStringBuilder(HtmlCompat().fromHtml(value, HtmlCompat().FROM_HTML_MODE_COMPACT)); + if (value) { + nativeView.setText((com as any).nativescript.label.Font.stringBuilderFromHtmlString(context, fontPath, value)); } else { - spannableStringBuilder = createSpannableStringBuilder(android.text.Html.fromHtml(value)); + nativeView.setText(null); } - nativeView.setText(spannableStringBuilder as any); - + } + [htmlProperty.setNative](value: string) { + this.setHtml(value); // textProperty.nativeValueChange(this, value === null || value === undefined ? '' : value.toString()); } @@ -324,44 +325,44 @@ export function getTransformedText(text: string, textTransform: TextTransform): } } -const createSpannableStringBuilder = profile('createSpannableStringBuilder', function createSpannableStringBuilder(spannedString: android.text.Spanned): android.text.SpannableStringBuilder { - if (!spannedString) { - return null; - } - const builder = new android.text.SpannableStringBuilder(spannedString as any); - const spans: native.Array = builder.getSpans(0, builder.length(), android.text.style.TypefaceSpan.class); - for (let index = 0; index < spans.length; index++) { - const span = spans[index]; - const start = builder.getSpanStart(span); - const end = builder.getSpanEnd(span); - const fontFamily = span.getFamily(); - const style = fontFamily.split('-')[1] || builder.removeSpan(span); - const font = new Font(fontFamily, 0, style === 'italic' ? 'italic' : 'normal', style === 'bold' ? 'bold' : 'normal'); - const typeface = font.getAndroidTypeface() || android.graphics.Typeface.create(fontFamily, 0); - const typefaceSpan: android.text.style.TypefaceSpan = new org.nativescript.widgets.CustomTypefaceSpan(fontFamily, typeface); - builder.setSpan(typefaceSpan, start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } +// const createSpannableStringBuilder = profile('createSpannableStringBuilder', function createSpannableStringBuilder(spannedString: android.text.Spanned): android.text.SpannableStringBuilder { +// if (!spannedString) { +// return null; +// } +// const builder = new android.text.SpannableStringBuilder(spannedString as any); +// const spans: native.Array = builder.getSpans(0, builder.length(), android.text.style.TypefaceSpan.class); +// for (let index = 0; index < spans.length; index++) { +// const span = spans[index]; +// const start = builder.getSpanStart(span); +// const end = builder.getSpanEnd(span); +// const fontFamily = span.getFamily(); +// const style = fontFamily.split('-')[1] || builder.removeSpan(span); +// const font = new Font(fontFamily, 0, style === 'italic' ? 'italic' : 'normal', style === 'bold' ? 'bold' : 'normal'); +// const typeface = font.getAndroidTypeface() || android.graphics.Typeface.create(fontFamily, 0); +// const typefaceSpan: android.text.style.TypefaceSpan = new org.nativescript.widgets.CustomTypefaceSpan(fontFamily, typeface); +// builder.setSpan(typefaceSpan, start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } + +// // const ssb = new android.text.SpannableStringBuilder(); +// // for (let i = 0, spanStart = 0, spanLength = 0, length = formattedString.spans.length; i < length; i++) { +// // const span = formattedString.spans.getItem(i); +// // const text = span.text; +// // const textTransform = (formattedString.parent).textTransform; +// // let spanText = (text === null || text === undefined) ? "" : text.toString(); +// // if (textTransform && textTransform !== "none") { +// // spanText = getTransformedText(spanText, textTransform); +// // } + +// // spanLength = spanText.length; +// // if (spanLength > 0) { +// // ssb.insert(spanStart, spanText); +// // setSpanModifiers(ssb, span, spanStart, spanStart + spanLength); +// // spanStart += spanLength; +// // } +// // } - // const ssb = new android.text.SpannableStringBuilder(); - // for (let i = 0, spanStart = 0, spanLength = 0, length = formattedString.spans.length; i < length; i++) { - // const span = formattedString.spans.getItem(i); - // const text = span.text; - // const textTransform = (formattedString.parent).textTransform; - // let spanText = (text === null || text === undefined) ? "" : text.toString(); - // if (textTransform && textTransform !== "none") { - // spanText = getTransformedText(spanText, textTransform); - // } - - // spanLength = spanText.length; - // if (spanLength > 0) { - // ssb.insert(spanStart, spanText); - // setSpanModifiers(ssb, span, spanStart, spanStart + spanLength); - // spanStart += spanLength; - // } - // } - - return builder; -}) +// return builder; +// }); // function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, start: number, end: number): void { // const spanStyle = span.style; diff --git a/src/typings/android.d.ts b/src/typings/android.d.ts new file mode 100644 index 0000000..da2d3e9 --- /dev/null +++ b/src/typings/android.d.ts @@ -0,0 +1,11 @@ +declare module com { + export module lsjwzh { + export module widget { + export module text { + export class FastTextView extends globalAndroid.view.View { + + } + } + } + } +} \ No newline at end of file