I have added links to text that is surrounded by [square brackets] in a popup dialog box. The links however, are not clickable (nothing happens when they are pressed). I can't work out why(!)
Here is my dialog box activity:
public void popupDefinition(CharSequence term, LinkedHashMap<String, String> dictionaryMap){
SpannableString definition = new SpannableString(dictionaryMap.get(term)); // grab the definition by checking against the dictionary map hash
Linkify.addLinks(definition, pattern, scheme); // find text in square brackets, add links
AlertDialog alertDialog = new AlertDialog.Builder(ListProjectActivity.this).create(); // create a dialog box
alertDialog.setMessage(definitionFormatted); // set dialog box message
alertDialog.show(); // actually display the dialog box
}
'scheme' and 'pattern' are defined earlier, as follows:
final static Pattern pattern = Pattern.compile("\\[[^]]*]"); // defines the fact that links are bound by [square brackets]
final String scheme = "http://example.com/"; // THIS IS NOT WORKING
Why, when I tap the links that appear (they appear nicely underlined in blue), do they not cause any response?
I'm not actually trying to launch URL links (I'll be redirecting the ACTION_VIEW intent when it does occur), but I need to confirm that some sort of response is happening before I get to that...
I'm not actually trying to launch URL links
Since you don't need to use URLs, don't bother with trying to create a custom Linkify scheme since it only creates URLSpans. You simply want to start an Activity from a keyword in a TextView. As I stated in one of your similar, but not duplicate, questions I would use a custom span, introducing ActivitySpan:
public class ActivitySpan extends ClickableSpan {
String keyword;
public ActivitySpan(String keyword) {
super();
this.keyword = keyword;
}
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, AnotherActivity.class);
intent.putExtra("keyword", keyword);
context.startActivity(intent);
}
}
There are no bells or whistles here, this span takes a [keyword] that you surrounded in brackets and passes it to the another Activity.
While I don't like the idea of using Linkify because of URLSpans, its pattern matching and span creation is great, so I copied and modified it:
private void addLinks(TextView textView, Pattern pattern) {
SpannableString spannable = SpannableString.valueOf(textView.getText());
Matcher matcher = pattern.matcher(spannable);
// Create ActivitySpans for each match
while (matcher.find())
spannable.setSpan(new ActivitySpan(matcher.group()), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Set new spans in TextView
textView.setText(spannable);
// Listen for spannable clicks, if not already
MovementMethod m = textView.getMovementMethod();
if ((m == null) || !(m instanceof LinkMovementMethod)) {
if (textView.getLinksClickable()) {
textView.setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
As a note, this method doesn't remove the [brackets] surrounding each keyword, but you can easily do this in the while-loop.
To use this, simply pass a TextView and Pattern to addLinks() in onCreate() and Voila!
A working example for you:
public class Example extends Activity {
Pattern pattern = Pattern.compile("\\[[^]]*]");
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
popupDefinition("Example: A [pattern] or [model], as of something to be [imitated] or [avoided]");
}
// It seems like you can call "popupDefinition(dictionaryMap.get(term));" rather than pass both.
public void popupDefinition(String string){
SpannableString spannable = new SpannableString(string);
Matcher matcher = pattern.matcher(spannable);
// Create ActivitySpans for each match
while (matcher.find())
spannable.setSpan(new ActivitySpan(matcher.group()), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Create a new TextView with these spans and enable the clickable links
TextView textView = new TextView(this);
textView.setText(spannable);
textView.setMovementMethod(LinkMovementMethod.getInstance());
// Create and display an AlertDialog with this TextView
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setView(textView)
.create();
alertDialog.show();
}
public class ActivitySpan extends ClickableSpan {
String keyword;
public ActivitySpan(String keyword) {
super();
this.keyword = keyword;
}
#Override
public void onClick(View v) {
Context context = v.getContext();
Toast.makeText(context, keyword, Toast.LENGTH_SHORT).show();
}
}
}
Related
I would like to link some text on a TextView to an Activity. This is the TextView that I have:
<TextView
android:id="#+id/termsLink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/terms"
android:layout_weight="4"/>
where #string/terms is:
<string name="terms">Accept terms & conditions..</string>
If I had a link to a webpage I would do it like this:
TextView link = (TextView) findViewById(R.id.termsLink);
link.setMovementMethod(LinkMovementMethod.getInstance());
but I do not know how to start an Activity when I press the link as when it is a real link (that it links a webpage).
EDIT: Please note that I do not have to handle the onClick event in the full text because the link is only on the part "terms & conditions".
EDIT 2: I have tried using two TextView as suggested on the comments and one of the answers below to make the same effect. But sometimes (depending on the screen) the "terms & conditions" part occupy two lines because it does not fill properly on the available space so the second line it is shown on the second TextView and not on the begining of the second line.
The effect is similar to this:
Accept terms &
conditions.
and I would like that it would be like this:
Accept terms &
conditions.
Thanks in advance!
Create a helper class with inner onClick listener
public class ClickSpan extends ClickableSpan {
private String url;
private OnClickListener listener;
public ClickSpan(String url, OnClickListener listener) {
this.url = url;
this.listener = listener;
}
#Override
public void onClick(View widget) {
if (listener != null) listener.onClick(url);
}
public interface OnClickListener {
void onClick(String url);
}
}
Then convert existing span into clickable one
public static Spannable createClickableSpans(Spanned original, ClickSpan.OnClickListener listener) {
SpannableString result = new SpannableString(original);
URLSpan[] spans = result.getSpans(0, result.length(), URLSpan.class);
for (URLSpan span : spans) {
int start = result.getSpanStart(span);
int end = result.getSpanEnd(span);
int flags = result.getSpanFlags(span);
result.removeSpan(span);
result.setSpan(new ClickSpan(span.getURL(), listener), start, end, flags);
}
return result;
}
So, final usage would be like
TextView link = (TextView) findViewById(R.id.termsLink);
link.setMovementMethod(LinkMovementMethod.getInstance());
link.setText(createClickableSpans((Spanned)link.getText(), new ClickSpan.OnClickListener(){
#Override
public void onClick(String url){
//Handle URL on text view click
}
}));
To make only part of a TextView clickable, you can use a spannable inside the TextView and set an onClick event listener. From here, you launch the activity with an intent as usual. You can limit the clickable section of the text by specifying the character positions (start to end)
Checkout this answer by #becomputer06
How to set the part of the text view is clickable
You should probably separate the text into 2 text views one with the terms and condition and one with just the accept.It would make things cleaner and easier. The following TextView is assuming its just for accept.
In the layouts corresponding java class(example: activity_main -> MainActivity):
public void start_activity(View view){
Intent newActivityIntent = new Intent(this,NewActivity.class);
startActivity(newActivityIntent);
}
NewActivity.class is just the name of the activity you want to start.
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
....
}
public void start_activity(View view){
.....
}
}
I am using a text view in my aap, which is having plane text as well as a hyperlink. Now when I click on hyperlink then link open with default browser. But in actual I dont want to open default browser. Actually I want to register OnClickListener on hyperlink and want to perform other.
I searched on internet and I got this solution...
Control onclicklistener in autolink enabled textview
But this is not helpful for me.
Anyone can tell me that how I can perform this.
Thanks in advance
you can use a Spannable object
final Spannable span = new SpannableString(text);
span.setSpan(new ClickableSpan() {
#Override
public void onClick(View v) {
}
}, 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
where text is your hyperlink
Remove android:autoLink="web" if this property setted into XML.
TextView textView =(TextView)findViewById(R.id.textView);
textView.setClickable(true);
when you want to open in browser use this code
textView.setMovementMethod(LinkMovementMethod.getInstance());
String text = "<a href='http://www.google.com'> Google </a>";
textView.setText(Html.fromHtml(text));
if you want to perform some operation register onclick listener for textview and perform.
textView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
Try doing this add
in your
main.xml
<TextView
android:id="#+id/yourTVID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="performSomeAction" />
in your SomeActivity.java
public void performSomeAction( View v){
//Perform your action
}
Try this, it should solve your problem. This method will return a Spannable String which have part of it clickable.
Before calling the below method you should Create CharSequence from the String then convert it to Spannable
CharSequence charSequencce = testView.getText();
Spannable spannable = (Spannable) charSequencce;
public SpannableStringBuilder addClickToPartsOfString(Spannable charSequence, String[] stringsToAddClick, final OnHyperLinkClickListener onClickListener) {
SpannableStringBuilder ssb = new SpannableStringBuilder(charSequence);
for(final String s : stringsToAddClick) {
int index1 = charSequence.toString().indexOf(s);
int index2 = (s.length() + index1);
ssb.setSpan(new ClickableSpan() {
#Override
public void onClick(View widget) {
onClickListener.onClick(s);
}
}, index1, index2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return ssb;
}
I've just made a library aiming to simplify this. See Textoo. You can achieve the same with code like:
TextView locNotFound = Textoo
.config((TextView) findViewById(R.id.view_location_disabled))
.addLinksHandler(new LinksHandler() {
#Override
public boolean onClick(View view, String url) {
if ("internal://settings/location".equals(url)) {
Intent locSettings = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(locSettings);
return true;
} else {
return false;
}
}
})
.apply();
Internally the library converts existing links in your textview / string resources (android system parse html links in string resources into Span for you already) into custom ClickableSpan and capture clicks into calls to your handlers.
This relieve you from having to calculate and hard coding the position of clickable spans to add. Thus make it easier to externalize your text into string resources and better for localization.
I'm using Mariotti's CardsLib to create a list with Cards. I create cards (in a for loop) as shown below:
CustomCard aCard = new CustomCard(getActivity().getApplicationContext());
CardHeader aHeader = new CardHeader(getActivity().getApplicationContext());
aHeader.setTitle(item.getHeaderText());
aCard.addCardHeader(aHeader);
aCard.setSpannableMainTitle(true);
aCard.setmSsbTitle(CustomSpannableBuilderForHome.getSpannedText(item));
CardThumbnail thumbnail = new CardThumbnail(getActivity().getApplicationContext());
thumbnail.setDrawableResource(R.drawable.ic_launcher);
aCard.addCardThumbnail(thumbnail);
cards.add(0, aCard);
CustomCard is a class extending Card class. I created this class because I need to assign an SpannableStringBuilder instance, instead of simple String. This is CustomCard:
public class CustomCard extends Card {
protected boolean isSpannableMainTitle;
/**
* Main Title
*/
protected SpannableStringBuilder mSsbTitle;
public SpannableStringBuilder getmSsbTitle() {
return mSsbTitle;
}
public void setmSsbTitle(SpannableStringBuilder mSsbTitle) {
this.mSsbTitle = mSsbTitle;
}
public boolean isSpannableMainTitle() {
return isSpannableMainTitle;
}
public void setSpannableMainTitle(boolean isSpannableMainTitle) {
this.isSpannableMainTitle = isSpannableMainTitle;
}
public CustomCard(Context context) {
super(context);
}
#Override
public void setupInnerViewElements(ViewGroup parent, View view) {
//Add simple title to header
if (view != null) {
TextView mTitleView = (TextView) view.findViewById(R.id.card_main_inner_simple_title);
if (mTitleView != null)
{
mTitleView.setText(isSpannableMainTitle? mSsbTitle : mTitle);
if (isSpannableMainTitle) mTitleView.setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
}
Everything worked fine prior to using maps api V2. Today, the card's title does not appear when the text does not contain spans. If it does have spans, only spans are shown. It is also worth mentioning that the title to be set, is ALWAYS an instance of SpannableStringBuilder.
For example:
Text1 = This is a text with no spans
Text2 = This is a (text) with (spans) <--- where () denote the presence of span.
This is the output:
Card 1:
Picture + Header + no text
Card 2:
Picture + Header + (text) (spans)
That is, the first card has no text, while the latter displays only spanned text (i.e. This is a with, all of them ignored).
As I said, this behavior started once I included Maps API V2. Please help!
My code looks like this,
String content = "Hi this is #Naveen. I'll meet #Peter in the evening.. Would you like to join #Sam??";
TextView contentTextView=(TextView)userHeader.findViewById(R.id.contentTextView);
contentTextView.setText(content);
Before setting the text in the textview, I would like to add click event for #Naveen, #Peter and #Sam.. When the user taps on these texts I want to open a new intent.. Is that possible? Any pointers would be quite helpful.
You can try to use Linkify with custom patterns.
However, if that doesn't suit your needs you can try this:
SpannableString ss = new SpannableString("Hi this is #Naveen. I'll meet #Peter in the evening.. Would you like to join #Sam??");
ClickableSpan clickableSpanNaveen = new ClickableSpan() {
#Override
public void onClick(View textView) {
//Do Stuff for naveen
}
};
ClickableSpan clickableSpanPeter = new ClickableSpan() {
#Override
public void onClick(View textView) {
//Do Stuff for peter
}
};
ClickableSpan clickableSpanSam = new ClickableSpan() {
#Override
public void onClick(View textView) {
//Do Stuff for sam
}
};
ss.setSpan(clickableSpanNaveen, 11, 17, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(clickableSpanPeter, 29, 35, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(clickableSpanSam, 76, 79, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView contentTextView=(TextView)userHeader.findViewById(R.id.contentTextView);
contentTextView.setText(ss);
contentTextView.setMovementMethod(LinkMovementMethod.getInstance());
You can use the Linkify class, which provides methods for adding links to TextViews. You can use the addLinks(TextView textView, Pattern pattern, String scheme) method, which needs you to specify the TextView, a Pattern for the text you want to match and a custom scheme that will be used to match Activities that can work with this kind of data. The Activity that you want to be opened when clicking on links must declare this scheme in its intent-filter. Hope this helps.
I've been using the android:autoLink just fine for formatting links and such, but I need to use android:onClick so I can't use that in this case. The reasoning is that I find it too easy to click on a phone number accidentally, so I'm going to intercept the click with a confirmation Dialog and then call.
Is there an easy way to still make the phone number in my TextView look like a normal clickable link? I poked around the Android source code, but couldn't find any particular style for me to reference.
This is the shortest solution:
final CharSequence text = tv.getText();
final SpannableString spannableString = new SpannableString( text );
spannableString.setSpan(new URLSpan(""), 0, spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(spannableString, TextView.BufferType.SPANNABLE);
Sadly, the effect of clicking doesn't show up as being clicked on a real url link, but you can overcome it like so:
final CharSequence text = tv.getText();
final SpannableString notClickedString = new SpannableString(text);
notClickedString.setSpan(new URLSpan(""), 0, notClickedString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(notClickedString, TextView.BufferType.SPANNABLE);
final SpannableString clickedString = new SpannableString(notClickedString);
clickedString.setSpan(new BackgroundColorSpan(Color.GRAY), 0, notClickedString.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
tv.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(final View v, final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
tv.setText(clickedString);
break;
case MotionEvent.ACTION_UP:
tv.setText(notClickedString, TextView.BufferType.SPANNABLE);
v.performClick();
break;
case MotionEvent.ACTION_CANCEL:
tv.setText(notClickedString, TextView.BufferType.SPANNABLE);
break;
}
return true;
}
});
Another solution is to use Html.fromHtml(...) , where the text inside has links tags ("") .
If you wish for another solution, check this post.
You can create a colors.xml resource file, what contains colors. Please take a look at Colors
If you want to underline your text, then please take a look at this post:
Underline
Don't forget to add android:clickable="true" or setClickable(true) to
your TextViews to make them clickable!
Linkify is a great class, it hunts for complex patterns like URLs, phone numbers, etc and turns them into URLSpans. Rather than re-write the existing regular expressions I extended the URLSpan class and created a method to upgrade only the telephone URLSpans to a custom URLSpan with a confirmation dialog.
First my extended URLSpan class, ConfirmSpan:
class ConfirmSpan extends URLSpan {
AlertDialog dialog;
View mView;
public ConfirmSpan(URLSpan span) {
super(span.getURL());
}
#Override
public void onClick(View widget) {
mView = widget;
if(dialog == null) {
AlertDialog.Builder mBuilder = new AlertDialog.Builder(widget.getContext());
mBuilder.setMessage("Do you want to call: " + getURL().substring(4) + "?");
mBuilder.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
})
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
openURL();
}
});
dialog = mBuilder.create();
}
dialog.show();
}
public void openURL() {
super.onClick(mView);
}
}
Next the method to swap out the different span classes:
private void swapSpans(TextView textView) {
Spannable spannable = (Spannable) textView.getText();
URLSpan[] spans = textView.getUrls();
for(URLSpan span : spans) {
if(span.getURL().toString().startsWith("tel:")) {
spannable.setSpan(new ConfirmSpan(span), spannable.getSpanStart(span), spannable.getSpanEnd(span), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.removeSpan(span);
}
}
}
Finally all you need to do is create a TextView with the autoLink attribute:
android:autoLink="phone"
And remember to call the swapSpans() method. Understand that I wrote this for fun, there may be other methods of doing this but I am unaware of them at the moment. Hope this helps!
To underline your TextView's text, you have to do something like:
final TextView text = (TextView) findViewById(R.id.text);
SpannableString string = new SpannableString("This is the uderlined text.");
string.setSpan(new UnderlineSpan(), 0, string.length(), 0);
text.setText(string);
This should work. Let me know about your progress.
With kotlin extension function (if you don't need the click effect as on a real link)
fun TextView.hyperlinkStyle() {
setText(
SpannableString(text).apply {
setSpan(
URLSpan(""),
0,
length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
},
TextView.BufferType.SPANNABLE
)
}
How to use
yourTextView.hyperlinkStyle()
Have a better answer.This is what i did.
final SpannableString ss = new SpannableString("Click here to verify Benificiary");
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View textView) {
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
}
};
ss.setSpan(clickableSpan,0,ss.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.BLUE);
You go anywhere you like when user clicks on the link through onclick method of ClickableSpan
Simply underline it:
val myText = "Text to be underlined"
textView.text = Html.fromHtml("<u>$myText</u>")
or with kotlin extensions:
fun TextView.underline() {
text = Html.fromHtml("<u>${text}</u>")
}
usage:
textView.text = myText
textView.underline()
More ways to style text in android here: https://medium.com/androiddevelopers/spantastic-text-styling-with-spans-17b0c16b4568