mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user