如果您想要做的是类似于 Twitter 的主题标签/用户名链接(处理纯文本并添加到特殊段的链接),您可以扩展 Label 来做到这一点。只需在文本中搜索特殊单词(这里我使用了正则表达式模式,但您可以使用任何其他方法)并将它们替换为链接的 HTML。
在本例中,isInternalRef
创建一个指向 Ajax 行为的链接。它还可用于创建指向资源、页面和其他类型组件的链接(有些处理直接链接,有些不处理)。
这个类是一个非常简单的例子,它并不打算支持所有的 Twitter 语法。
public class TweetLabel extends Label {
private static final Pattern USERNAME_HASHTAG_PATTERN = Pattern.compile("\\B([@#*])([a-zA-Z0-9_]+)", Pattern.CASE_INSENSITIVE);
private transient CharSequence body;
public TweetLabel(String id) {
super(id);
}
public TweetLabel(String id, String label) {
super(id, label);
}
public TweetLabel(String id, Serializable label) {
super(id, label);
}
public TweetLabel(String id, IModel<?> model) {
super(id, model);
}
protected void onLinkClicked(AjaxRequestTarget target, String word) {
target.appendJavaScript("alert('You clicked on \"" + JavaScriptUtils.escapeQuotes(word) + "\", and the server knows...');");
}
@Override
protected void onConfigure() {
super.onConfigure();
// process text here, because we can add behaviors here, but not at onComponentTagBody()
body = processText(getDefaultModelObjectAsString());
}
@Override
public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) {
replaceComponentTagBody(markupStream, openTag, body);
}
/**
* based on Matcher.replaceAll()
*/
private CharSequence processText(String text) {
Matcher m = USERNAME_HASHTAG_PATTERN.matcher(text);
boolean result = m.find();
if (result) {
StringBuffer sb = new StringBuffer();
do {
final String replacement = getLinkedTextAndAddBehavior(text, m.start(), m.end());
m.appendReplacement(sb, replacement);
result = m.find();
} while (result);
m.appendTail(sb);
return sb;
}
return text;
}
private String getLinkedTextAndAddBehavior(String fullText, int start, int end) {
final String matchedString = fullText.substring(start, end);
final int length = matchedString.length();
final String prefix = matchedString.substring(0, 1);
final String identifier = matchedString.substring(1, length);
final boolean isUsername = prefix.equals("@");
final boolean isHashtag = prefix.equals("#") && (start == 0 || fullText.charAt(start - 1) != '&');
final boolean isInternalRef = prefix.equals("*");
final String replacement;
if (isUsername) {
final String url = "https://twitter.com/" + identifier;
replacement = "<a href='" + url + "'>" + matchedString + "</a>";
} else if (isHashtag) {
final String url = "https://twitter.com/search?src=hash&q=" + UrlEncoder.QUERY_INSTANCE.encode(matchedString, "UTF-8");
replacement = "<a href='" + url + "'>" + matchedString + "</a>";
} else if (isInternalRef) {
final LinkedWordBehavior behavior = getOrAddBehavior(new LinkedWordBehavior(identifier));
final String rawFunction = behavior.getCallbackScript().toString();
replacement = String.format("<a href='#' onclick='%s;return false;'>%s</a>", rawFunction, matchedString);
} else {
replacement = matchedString;
}
return replacement;
}
/**
* Verify if the behavior was already added, add if not.
*/
private LinkedWordBehavior getOrAddBehavior(LinkedWordBehavior behavior) {
final List<LinkedWordBehavior> behaviors = getBehaviors(LinkedWordBehavior.class);
final int index = behaviors.indexOf(behavior);
if (index > -1) {
return behaviors.get(index);
} else {
add(behavior);
return behavior;
}
}
private final class LinkedWordBehavior extends AbstractDefaultAjaxBehavior {
private final String word;
public LinkedWordBehavior(String word) {
this.word = word;
}
@Override
protected void respond(AjaxRequestTarget target) {
final String word = TweetLabel.this.getRequest().getRequestParameters().getParameterValue("word").toString();
if (!Strings.isEmpty(word)) {
TweetLabel.this.onLinkClicked(target, word);
}
}
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
attributes.getExtraParameters().put("word", word);
}
@Override
public int hashCode() {
return word.hashCode();
}
@Override
public boolean equals(Object obj) {
return word.equals(((LinkedWordBehavior) obj).word);
}
}
}
更新更改了示例以处理带有行为的 ajax 请求。还有其他方法可以做到这一点。例如,您可以使用单一行为来处理所有链接,或使用资源,但这种方式对我来说看起来更干净。