J'ai le texte " Android est une pile de logiciels ". Dans ce texte, je veux définir le " pile " texte est cliquable. en ce sens, si vous cliquez dessus, il sera redirigé vers une nouvelle activité (pas dans le navigateur).
J'ai essayé mais je ne comprends pas.
Android.text.style.ClickableSpan
peut résoudre votre problème.
SpannableString ss = new SpannableString("Android is a Software stack");
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View textView) {
startActivity(new Intent(MyActivity.this, NextActivity.class));
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
}
};
ss.setSpan(clickableSpan, 22, 27, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView textView = (TextView) findViewById(R.id.hello);
textView.setText(ss);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.TRANSPARENT);
// En XML: TextView: Android: textColorLink = "@ drawable/your_selector"
Ma fonction pour créer plusieurs liens dans TextView
public void makeLinks(TextView textView, String[] links, ClickableSpan[] clickableSpans) {
SpannableString spannableString = new SpannableString(textView.getText());
for (int i = 0; i < links.length; i++) {
ClickableSpan clickableSpan = clickableSpans[i];
String link = links[i];
int startIndexOfLink = textView.getText().toString().indexOf(link);
spannableString.setSpan(clickableSpan, startIndexOfLink,
startIndexOfLink + link.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
textView.setHighlightColor(
Color.TRANSPARENT); // prevent TextView change background when highlight
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setText(spannableString, TextView.BufferType.SPANNABLE);
}
EN UTILISANT
textView.setLinkTextColor(Color.BLUE); // default link color for clickable span, we can also set it in xml by Android:textColorLink=""
ClickableSpan normalLinkClickSpan = new ClickableSpan() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "Normal Link", Toast.LENGTH_SHORT).show();
}
};
ClickableSpan noUnderLineClickSpan = new ClickableSpan() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "NoUnderLine Link", Toast.LENGTH_SHORT)
.show();
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
ds.setColor(Color.Magenta); // specific color for this link
}
};
ClickableSpan highlightClickSpan = new ClickableSpan() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "Highlight Link", Toast.LENGTH_SHORT)
.show();
view.invalidate(); // need put invalidate here to make text change to GREEN after clicked
}
@Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(false);
if (textView.isPressed() && textView.getSelectionStart() != -1 && textView.getText()
.toString()
.substring(textView.getSelectionStart(), textView.getSelectionEnd())
.equals("Highlight Link")) {
ds.setColor(Color.RED); // need put invalidate here to make text change to RED when pressed on Highlight Link textView.invalidate();
} else {
ds.setColor(Color.GREEN);
}
// dont put invalidate here because if you put invalidate here `updateDrawState` will called forever
}
};
makeLinks(textView, new String[] {
"Normal Link", "NoUnderLine Link", "Highlight Link"
}, new ClickableSpan[] {
normalLinkClickSpan, noUnderLineClickSpan, highlightClickSpan
});
XML
<TextView
Android:id="@+id/myText"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="Please read Normal Link and NoUnderLine Link and Highlight Link"
Android:textSize="30sp"
/>
Vous pouvez utiliser ClickableSpan comme décrit dans ce post
TextView myTextView = new TextView(this);
String myString = "Some text [clickable]";
int i1 = myString.indexOf("[");
int i2 = myString.indexOf("]");
myTextView.setMovementMethod(LinkMovementMethod.getInstance());
myTextView.setText(myString, BufferType.SPANNABLE);
Spannable mySpannable = (Spannable)myTextView.getText();
ClickableSpan myClickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) { /* do something */ }
};
mySpannable.setSpan(myClickableSpan, i1, i2 + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Vous pouvez utiliser un exemple de code. Vous voulez en savoir plus sur ClickableSpan. Veuillez vérifier this documentaion
SpannableString myString = new SpannableString("This is example");
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View textView) {
ToastUtil.show(getContext(),"Clicked Smile ");
}
};
//For Click
myString.setSpan(clickableSpan,startIndex,lastIndex,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
//For UnderLine
myString.setSpan(new UnderlineSpan(),startIndex,lastIndex,0);
//For Bold
myString.setSpan(new StyleSpan(Typeface.BOLD),startIndex,lastIndex,0);
//Finally you can set to textView.
TextView textView = (TextView) findViewById(R.id.txtSpan);
textView.setText(myString);
textView.setMovementMethod(LinkMovementMethod.getInstance());
J'ai créé cette méthode d'assistance au cas où quelqu'un aurait besoin d'une position de début et de fin à partir d'une chaîne.
public static TextView createLink(TextView targetTextView, String completeString,
String partToClick, ClickableSpan clickableAction) {
SpannableString spannableString = new SpannableString(completeString);
// make sure the String is exist, if it doesn't exist
// it will throw IndexOutOfBoundException
int startPosition = completeString.indexOf(partToClick);
int endPosition = completeString.lastIndexOf(partToClick) + partToClick.length();
spannableString.setSpan(clickableAction, startPosition, endPosition,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
targetTextView.setText(spannableString);
targetTextView.setMovementMethod(LinkMovementMethod.getInstance());
return targetTextView;
}
Et voici comment vous l'utilisez
private void initSignUp() {
String completeString = "New to Reddit? Sign up here.";
String partToClick = "Sign up";
ClickableTextUtil
.createLink(signUpEditText, completeString, partToClick,
new ClickableSpan() {
@Override
public void onClick(View widget) {
// your action
Toast.makeText(activity, "Start Sign up activity",
Toast.LENGTH_SHORT).show();
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
// this is where you set link color, underline, typeface etc.
int linkColor = ContextCompat.getColor(activity, R.color.blumine);
ds.setColor(linkColor);
ds.setUnderlineText(false);
}
});
}
t= (TextView) findViewById(R.id.PP1);
t.setText(Html.fromHtml("<bThis is normal text </b>" +
"<a href=\"http://www.xyz-zyyx.com\">This is cliclable text</a> "));
t.setMovementMethod(LinkMovementMethod.getInstance());
Version Kotlin de la réponse de Phan Van Linh.
S'il vous plaît noter qu'il a quelques modifications mineures.
fun makeLinks(textView: TextView, links: Array<String>, clickableSpans: Array<ClickableSpan>) {
val spannableString = SpannableString(textView.text)
for (i in links.indices) {
val clickableSpan = clickableSpans[i]
val link = links[i]
val startIndexOfLink = textView.text.indexOf(link)
spannableString.setSpan(clickableSpan, startIndexOfLink, startIndexOfLink + link.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
textView.movementMethod = LinkMovementMethod.getInstance()
textView.setText(spannableString, TextView.BufferType.SPANNABLE)
}
fun setupClickableTextView() {
val termsOfServicesClick = object : ClickableSpan() {
override fun onClick(p0: View?) {
Toast.makeText(applicationContext, "ToS clicked", Toast.LENGTH_SHORT).show()
}
}
val privacyPolicyClick = object : ClickableSpan() {
override fun onClick(p0: View?) {
Toast.makeText(applicationContext, "PP clicked", Toast.LENGTH_SHORT).show()
}
}
makeLinks(termsTextView, arrayOf("terms", "privacy policy"), arrayOf(termsOfServicesClick, privacyPolicyClick))
}
Voici une méthode Kotlin
pour rendre des parties d'une TextView
cliquable:
private fun makeTextLink(textView: TextView, str: String, underlined: Boolean, color: Int?, action: (() -> Unit)? = null) {
val spannableString = SpannableString(textView.text)
val textColor = color ?: textView.currentTextColor
val clickableSpan = object : ClickableSpan() {
override fun onClick(textView: View) {
action?.invoke()
}
override fun updateDrawState(drawState: TextPaint) {
super.updateDrawState(drawState)
drawState.isUnderlineText = underlined
drawState.color = textColor
}
}
val index = spannableString.indexOf(str)
spannableString.setSpan(clickableSpan, index, index + str.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = spannableString
textView.movementMethod = LinkMovementMethod.getInstance()
textView.highlightColor = Color.TRANSPARENT
}
Il peut être appelé plusieurs fois pour créer plusieurs liens dans un TextView:
makeTextLink(myTextView, str, false, Color.RED, action = { Log.d("onClick", "link") })
makeTextLink(myTextView, str1, true, null, action = { Log.d("onClick", "link1") })
Pour audacieux,
mySpannable.setSpan(new StyleSpan(Typeface.BOLD),termStart,termStop,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Ceci est ma MovementMethod
pour détecter les clics sur lien/texte/image. Il est modifié LinkMovementMethod
.
import Android.text.Layout;
import Android.text.NoCopySpan;
import Android.text.Selection;
import Android.text.Spannable;
import Android.text.method.ScrollingMovementMethod;
import Android.text.style.ClickableSpan;
import Android.text.style.ImageSpan;
import Android.text.style.URLSpan;
import Android.view.KeyEvent;
import Android.view.MotionEvent;
import Android.view.View;
import Android.widget.TextView;
public class ClickMovementMethod extends ScrollingMovementMethod {
private Object FROM_BELOW = new NoCopySpan.Concrete();
private static final int CLICK = 1;
private static final int UP = 2;
private static final int DOWN = 3;
private Listener listener;
public void setListener(Listener listener) {
this.listener = listener;
}
@Override
public boolean canSelectArbitrarily() {
return true;
}
@Override
protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode,
int movementMetaState, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
if (event.getAction() == KeyEvent.ACTION_DOWN &&
event.getRepeatCount() == 0 && action(CLICK, widget, buffer)) {
return true;
}
}
break;
}
return super.handleMovementKey(widget, buffer, keyCode, movementMetaState, event);
}
@Override
protected boolean up(TextView widget, Spannable buffer) {
if (action(UP, widget, buffer)) {
return true;
}
return super.up(widget, buffer);
}
@Override
protected boolean down(TextView widget, Spannable buffer) {
if (action(DOWN, widget, buffer)) {
return true;
}
return super.down(widget, buffer);
}
@Override
protected boolean left(TextView widget, Spannable buffer) {
if (action(UP, widget, buffer)) {
return true;
}
return super.left(widget, buffer);
}
@Override
protected boolean right(TextView widget, Spannable buffer) {
if (action(DOWN, widget, buffer)) {
return true;
}
return super.right(widget, buffer);
}
private boolean action(int what, TextView widget, Spannable buffer) {
Layout layout = widget.getLayout();
int padding = widget.getTotalPaddingTop() +
widget.getTotalPaddingBottom();
int areatop = widget.getScrollY();
int areabot = areatop + widget.getHeight() - padding;
int linetop = layout.getLineForVertical(areatop);
int linebot = layout.getLineForVertical(areabot);
int first = layout.getLineStart(linetop);
int last = layout.getLineEnd(linebot);
ClickableSpan[] candidates = buffer.getSpans(first, last, URLSpan.class);
int a = Selection.getSelectionStart(buffer);
int b = Selection.getSelectionEnd(buffer);
int selStart = Math.min(a, b);
int selEnd = Math.max(a, b);
if (selStart < 0) {
if (buffer.getSpanStart(FROM_BELOW) >= 0) {
selStart = selEnd = buffer.length();
}
}
if (selStart > last)
selStart = selEnd = Integer.MAX_VALUE;
if (selEnd < first)
selStart = selEnd = -1;
switch (what) {
case CLICK:
if (selStart == selEnd) {
return false;
}
if (listener != null) {
URLSpan[] link = buffer.getSpans(selStart, selEnd, URLSpan.class);
if (link.length >= 1) {
listener.onClick(link[0].getURL());
} else {
ImageSpan[] image = buffer.getSpans(selStart, selEnd, ImageSpan.class);
if (image.length >= 1) {
listener.onImageClicked(image[0].getSource());
} else {
listener.onTextClicked();
}
}
}
break;
case UP:
int beststart, bestend;
beststart = -1;
bestend = -1;
for (int i = 0; i < candidates.length; i++) {
int end = buffer.getSpanEnd(candidates[i]);
if (end < selEnd || selStart == selEnd) {
if (end > bestend) {
beststart = buffer.getSpanStart(candidates[i]);
bestend = end;
}
}
}
if (beststart >= 0) {
Selection.setSelection(buffer, bestend, beststart);
return true;
}
break;
case DOWN:
beststart = Integer.MAX_VALUE;
bestend = Integer.MAX_VALUE;
for (int i = 0; i < candidates.length; i++) {
int start = buffer.getSpanStart(candidates[i]);
if (start > selStart || selStart == selEnd) {
if (start < beststart) {
beststart = start;
bestend = buffer.getSpanEnd(candidates[i]);
}
}
}
if (bestend < Integer.MAX_VALUE) {
Selection.setSelection(buffer, beststart, bestend);
return true;
}
break;
}
return false;
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
if (action == MotionEvent.ACTION_UP) {
if (listener != null) {
if (link.length >= 1) {
listener.onClick(link[0].getURL());
} else {
ImageSpan[] image = buffer.getSpans(off, off, ImageSpan.class);
if (image.length >= 1) {
listener.onImageClicked(image[0].getSource());
} else if (Selection.getSelectionStart(buffer) == Selection.getSelectionEnd(buffer)) {
listener.onTextClicked();
}
}
}
}
if (action == MotionEvent.ACTION_DOWN && link.length != 0) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
return true;
}
if (link.length == 0) {
Selection.removeSelection(buffer);
}
}
return super.onTouchEvent(widget, buffer, event);
}
@Override
public void initialize(TextView widget, Spannable text) {
Selection.removeSelection(text);
text.removeSpan(FROM_BELOW);
}
@Override
public void onTakeFocus(TextView view, Spannable text, int dir) {
Selection.removeSelection(text);
if ((dir & View.FOCUS_BACKWARD) != 0) {
text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT);
} else {
text.removeSpan(FROM_BELOW);
}
}
public interface Listener {
void onClick(String clicked);
void onTextClicked();
void onImageClicked(String source);
}
}
Je suggérerais une approche différente qui, selon moi, nécessite moins de code et est plus "conviviale pour la localisation".
En supposant que votre activité de destination s'appelle "ActivityStack", définissez dans le manifeste un filtre d'intention pour cette activité avec un schéma personnalisé (par exemple "myappscheme") dans AndroidManifest.xml:
<activity
Android:name=".ActivityStack">
<intent-filter>
<action Android:name="Android.intent.action.VIEW" />
<category Android:name="Android.intent.category.DEFAULT" />
<data Android:Host="stack"/>
<data Android:scheme="myappscheme" />
</intent-filter>
</activity>
Définissez le TextView sans balise spéciale (il est important de NE PAS utiliser la balise "Android: autoLink", voir: https://stackoverflow.com/a/20647011/1699702 ):
<TextView
Android:id="@+id/stackView"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="@string/stack_string" />
puis utilisez un lien avec un schéma personnalisé et un hôte dans le texte de TextView en tant que (dans String.xml):
<string name="stack_string">Android is a Software <a href="myappscheme://stack">stack</a></string>
et "activer" le lien avec setMovementMethod () (dans onCreate () pour les activités ou onCreateView () pour les fragments):
TextView stack = findViewById(R.id.stackView);
stack.setMovementMethod(LinkMovementMethod.getInstance());
Cela ouvrira l’activité de pile avec un tapotement sur le mot "pile".
Vous pouvez vous cette méthode pour définir la valeur cliquable
public void setClickableString(String clickableValue, String wholeValue, TextView yourTextView){
String value = wholeValue;
SpannableString spannableString = new SpannableString(value);
int startIndex = value.indexOf(clickableValue);
int endIndex = startIndex + clickableValue.length();
spannableString.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false); // <-- this will remove automatic underline in set span
}
@Override
public void onClick(View widget) {
// do what you want with clickable value
}
}, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
yourTextView.setText(spannableString);
yourTextView.setMovementMethod(LinkMovementMethod.getInstance()); // <-- important, onClick in ClickableSpan won't work without this
}
Voici comment l'utiliser:
TextView myTextView = findViewById(R.id.myTextView);
setClickableString("stack", "Android is a Software stack", myTextView);