feat: TappableSpan support (#8256)

* feat(android): clickable span

Initial support for clickable span on Android

* test: clickable-span test page

* remove console.log

* use _emit instead of notify

* rename clickable to tappable in Span

* updated NativeScript.api.md

* chore: fixing tslint errors

* chore: fixed witespacing

* moved and improved test page

* feat: tappable span iOS implementation

Co-authored-by: Eduardo Speroni <edusperoni@gmail.com>
This commit is contained in:
Vasil Trifonov
2020-03-20 18:35:28 +02:00
parent 92b5b02bf5
commit 8ab0e72bc9
6 changed files with 224 additions and 4 deletions

View File

@@ -51,6 +51,42 @@ function initializeTextTransformation(): void {
TextTransformation = TextTransformationImpl;
}
interface ClickableSpan {
new (owner: Span): android.text.style.ClickableSpan;
}
let ClickableSpan: ClickableSpan;
function initializeClickableSpan(): void {
if (ClickableSpan) {
return;
}
class ClickableSpanImpl extends android.text.style.ClickableSpan {
owner: WeakRef<Span>;
constructor(owner: Span) {
super();
this.owner = new WeakRef(owner);
return global.__native(this);
}
onClick(view: android.view.View): void {
const owner = this.owner.get();
if (owner) {
owner._emit(Span.linkTapEvent);
}
view.clearFocus();
view.invalidate();
}
updateDrawState(tp: android.text.TextPaint): void {
// don't style as link
}
}
ClickableSpan = ClickableSpanImpl;
}
export class TextBase extends TextBaseCommon {
nativeViewProtected: android.widget.TextView;
nativeTextViewProtected: android.widget.TextView;
@@ -60,12 +96,15 @@ export class TextBase extends TextBaseCommon {
private _maxHeight: number;
private _minLines: number;
private _maxLines: number;
private _tappable: boolean = false;
private _defaultMovementMethod: android.text.method.MovementMethod;
public initNativeView(): void {
super.initNativeView();
initializeTextTransformation();
const nativeView = this.nativeTextViewProtected;
this._defaultTransformationMethod = nativeView.getTransformationMethod();
this._defaultMovementMethod = this.nativeView.getMovementMethod();
this._minHeight = nativeView.getMinHeight();
this._maxHeight = nativeView.getMaxHeight();
this._minLines = nativeView.getMinLines();
@@ -112,6 +151,8 @@ export class TextBase extends TextBaseCommon {
return;
}
this._setTappableState(false);
this._setNativeText(reset);
}
@@ -131,6 +172,7 @@ export class TextBase extends TextBaseCommon {
const spannableStringBuilder = createSpannableStringBuilder(value);
nativeView.setText(<any>spannableStringBuilder);
this._setTappableState(isStringTappable(value));
textProperty.nativeValueChange(this, (value === null || value === undefined) ? "" : value.toString());
@@ -315,6 +357,19 @@ export class TextBase extends TextBaseCommon {
this.nativeTextViewProtected.setText(<any>transformedText);
}
_setTappableState(tappable: boolean) {
if (this._tappable !== tappable) {
this._tappable = tappable;
if (this._tappable) {
this.nativeViewProtected.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
this.nativeViewProtected.setHighlightColor(null);
}
else {
this.nativeViewProtected.setMovementMethod(this._defaultMovementMethod);
}
}
}
}
function getCapitalizedString(str: string): string {
@@ -346,6 +401,20 @@ export function getTransformedText(text: string, textTransform: TextTransform):
}
}
function isStringTappable(formattedString: FormattedString) {
if (!formattedString) {
return false;
}
for (let i = 0, length = formattedString.spans.length; i < length; i++) {
const span = formattedString.spans.getItem(i);
if (span.tappable) {
return true;
}
}
return false;
}
function createSpannableStringBuilder(formattedString: FormattedString): android.text.SpannableStringBuilder {
if (!formattedString || !formattedString.parent) {
return null;
@@ -444,6 +513,12 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span,
}
}
const tappable = span.tappable;
if (tappable) {
initializeClickableSpan();
ssb.setSpan(new ClickableSpan(span), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
// TODO: Implement letterSpacing for Span here.
// const letterSpacing = formattedString.parent.style.letterSpacing;
// if (letterSpacing > 0) {