我有文本“Android是一个软件堆栈”。在这个文本中,我想设置“堆栈”文本为可点击。所以,如果你点击它,它将重定向到一个新的活动(不在浏览器中)。

我试过了,但没有找到解决办法。


当前回答

Java解决方案(更新2022年)

特点:

允许多个点击时,有重复的词。 可以为每个重复的单词量身定制特定的命令。

我以daler445的代码为基础,允许对重复的单词使用多个可单击的命令。

在Java课上:

public class MainActivity extends AppCompatActivity {
    SharedPreferences sp;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sp = getSharedPreferences("MyUserPrefs", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        TextView fulltext = (TextView) findViewById(R.id.fulltext);

        //replace setText("") to setText("Android is a Software stack") for his case
        fulltext.setText("Search [1] this full [1] text with repeated strings. Search [2] Search [3] full [2] full [3]");

        List<Pair<String, View.OnClickListener>> links = new ArrayList<>();

        //replace "Search" with "stack" for his case
        String stringtohyperlink = "Search";
        String stringtohyperlink2 = "full";

        links.add(new Pair<>(stringtohyperlink, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String position = sp.getString("position","0");
                if (position.equals("1")) {
                    Toast.makeText(MainActivity.this, "Search 1 has been Clicked.", Toast.LENGTH_SHORT).show();
                    editor.putString("position","0");
                    editor.apply();
                }
                if (position.equals("2")) {
                    Toast.makeText(MainActivity.this, "Search 2 has been Clicked.", Toast.LENGTH_SHORT).show();
                    editor.putString("position","0");
                    editor.apply();
                }
                if (position.equals("3")) {
                    Toast.makeText(MainActivity.this, "Search 3 has been Clicked.", Toast.LENGTH_SHORT).show();
                    editor.putString("position","0");
                    editor.apply();
                }
            }
        }));

        links.add(new Pair<>(stringtohyperlink2, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String position = sp.getString("position","0");
                if (position.equals("1")) {
                    Toast.makeText(MainActivity.this, "full 1 has been Clicked.", Toast.LENGTH_SHORT).show();
                    editor.putString("position","0");
                    editor.apply();
                }
                if (position.equals("2")) {
                    Toast.makeText(MainActivity.this, "full 2 has been Clicked.", Toast.LENGTH_SHORT).show();
                    editor.putString("position","0");
                    editor.apply();
                }
                if (position.equals("3")) {
                    Toast.makeText(MainActivity.this, "full 3 has been Clicked.", Toast.LENGTH_SHORT).show();
                    editor.putString("position","0");
                    editor.apply();
                }
            }
        }));


        makeLinks(fulltext, links);


    }

    public void makeLinks(TextView textView, List<Pair<String, View.OnClickListener>> links) {
        SpannableString spannableString = new SpannableString(textView.getText().toString());

        int startIndexState = -1;
        SharedPreferences.Editor editor = sp.edit();

        for (Pair<String, View.OnClickListener> link : links) {
            ClickableSpan clickableSpan = new ClickableSpan() {
                @Override
                public void onClick(@NonNull View widget) {
                    editor.putString("position","1");
                    editor.apply();

                    widget.invalidate();
                    assert link.second != null;
                    link.second.onClick(widget);
                }
            };
            ClickableSpan clickableSpan2 = new ClickableSpan() {
                @Override
                public void onClick(@NonNull View widget) {
                    editor.putString("position","2");
                    editor.apply();

                    widget.invalidate();
                    assert link.second != null;
                    link.second.onClick(widget);
                }
            };

            ClickableSpan clickableSpan3 = new ClickableSpan() {
                @Override
                public void onClick(@NonNull View widget) {
                    editor.putString("position","3");
                    editor.apply();

                    widget.invalidate();
                    assert link.second != null;
                    link.second.onClick(widget);
                }
            };
            //... This only allows for 3 repeated words
            //... Add more of it, if there are more repeated words.

            assert link.first != null;
            int startIndexOfLink = textView.getText().toString().indexOf(link.first, startIndexState + 1);
            spannableString.setSpan(clickableSpan, startIndexOfLink, startIndexOfLink + link.first.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);

            int startIndexOfLink2 = textView.getText().toString().indexOf(link.first, startIndexOfLink + 1);
            if (startIndexOfLink2 != -1) {
                spannableString.setSpan(clickableSpan2, startIndexOfLink2, startIndexOfLink2 + link.first.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
            }

            int startIndexOfLink3 = textView.getText().toString().indexOf(link.first, startIndexOfLink2 + 1);
            if (startIndexOfLink3 != -1) {
                spannableString.setSpan(clickableSpan3, startIndexOfLink3, startIndexOfLink3 + link.first.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
            }
            //... This only allows for 3 repeated words
            //... Add more of it, if there are more repeated words.

            textView.setMovementMethod(LinkMovementMethod.getInstance());
            textView.setText(spannableString, TextView.BufferType.SPANNABLE);
        }
    }
}

在xml

  <TextView
        android:id="@+id/fulltext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

Demo

其他回答

你可以像本文中描述的那样使用ClickableSpan

示例代码:

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);

参考

它对文本的某些部分的可点击部分非常有帮助。

点是正则表达式中的一个特殊字符。如果你想要分隔圆点,需要将圆点转义为\\。而不是仅仅将"."传递给可扩展的文本方法。或者,也可以使用正则表达式[。在Java中通过一个点来分隔字符串。

Kotlin上复杂但通用的解决方案

  /*
    * Receive Pair of Text and Action and set it clickable and appearing as link
    * */
fun TextView.setClickableText(vararg textToSpanAndClickAction: Pair<String, (String) -> Unit>) {
    val builder = SpannableStringBuilder(text.toString())

    textToSpanAndClickAction.forEach { argPair ->
    val clickableSpan = object : ClickableSpan() {
        override fun onClick(widget: View) {
            argPair.second.invoke(argPair.first)
        }
    }

    this.text.toString().let { fullText ->
        val indexOfFirst = fullText.indexOf(argPair.first)
        val indexOfLast = indexOfFirst + argPair.first.length
        if (indexOfFirst < 0){
            //No match found
            return
        }else{
            builder.setSpan(
                clickableSpan,
                indexOfFirst,
                indexOfLast,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
    }
}

this.text = builder
    this.movementMethod = LinkMovementMethod.getInstance()
}

Kotlin Spannable

这是我的MovementMethod,用于检测链接/文本/图像点击。它被修改为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);

}

}

kotlin中更通用的答案

   fun setClickableText(view: TextView, firstSpan: String, secondSpan: String) {
    val context = view.context
    val builder = SpannableStringBuilder()
    val unClickableSpan = SpannableString(firstSpan)
    val span = SpannableString(" "+secondSpan)

    builder.append(unClickableSpan);
    val clickableSpan: ClickableSpan = object : ClickableSpan() {
        override fun onClick(textView: View) {
            val intent = Intent(context, HomeActivity::class.java)
         context.startActivity(intent)
        }

        override fun updateDrawState(ds: TextPaint) {
            super.updateDrawState(ds)
            ds.isUnderlineText = true
            ds.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC));
        }
    }
    builder.append(span);
    builder.setSpan(clickableSpan, firstSpan.length, firstSpan.length+secondSpan.length+1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

    view.setText(builder,TextView.BufferType.SPANNABLE)
    view.setMovementMethod(LinkMovementMethod.getInstance());


}