Why onClick() Method of ClickableSpan is called twice in android - android

onClick method is called twice, so when back from SecondActivity.class it will again reload it. I have a TextView named postTextView in which See More is clickable.
Where:
R.string.readMore = See More.
Here is the code which I've used.
String mTitleBody = Html.fromHtml(postBodyText).toString().substring(0, 150).trim();
mTitleBody = mTitleBody.concat("..." + mContext.getResources().getString(R.string.readMore)).replaceAll("<img.+?>|<IMG.+?>", "").replaceAll("\n", "<br/>");
int index1 = Html.fromHtml(mTitleBody).toString().trim().length() -
mContext.getResources().getString(R.string.readMore).length();
int index2 = Html.fromHtml(mTitleBody).toString().trim().length();
postTextView.setTextIsSelectable(true);
postTextView.setMovementMethod(LinkMovementMethod.getInstance());
postTextView.setText(Html.fromHtml(mTitleBody), TextView.BufferType.SPANNABLE);
Spannable mySpannable = (Spannable) postTextView.getText();
ClickableSpan myClickableSpan = new ClickableSpan() {
#Override
public void onClick(View view) {
Log.d("FirstClass", "onClick");
Intent intent = new Intent(mContext, SecondActivity.class);
(mContext).startActivity(intent);
((Activity) mContext).overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
ds.setColor(ContextCompat.getColor(mContext, R.color.body_text_3));
}
};
mySpannable.setSpan(myClickableSpan, index1, index2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Can anyone help me here, Thanks in advance.

If you are using the autolink property in the TextView then you need to set the TextView to not be focusable after setting the movement method.
To do this add the following line after postTextView.setMovementMethod(LinkMovementMethod.getInstance());:
postTextView.setFocusable(false);
An explanation is in the second paragraph from the Android setMovementMethod documentation:
Sets the MovementMethod for handling arrow key movement for this
TextView. This can be null to disallow using the arrow keys to move
the cursor or scroll the view.
Be warned that if you want a TextView with a key listener or movement
method not to be focusable, or if you want a TextView without a key
listener or movement method to be focusable, you must call
View.setFocusable(boolean) again after calling this to get the
focusability back the way you want it.
Link to the documentation:
https://developer.android.com/reference/android/widget/TextView#setMovementMethod(android.text.method.MovementMethod)

I had this exact same issue, it was because I had the "autolink: true" property in the respective view, removing it helped.

Related

SpannableStringBuilder not displaying text in Text View

I'm trying to make an array of strings individually clickable within a text view that is within a RecyclerView (each tag would pass different data, which is fetched from the api on load). I've created the string using SpannableStringBuilder as below within the bindView method.
fun bindView(link: PostsModel)
val tags = link.topics
var spans = SpannableStringBuilder()
for (tag in tags) {
val string = SpannableString(tag.name)
string.setSpan(ClickableTags(tag.name), 0, tag.name.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spans.append(string)
}
}
Then I set it to the text view.
view.headerTags.setText(spans, TextView.BufferType.SPANNABLE)
If I println() the contents of spans and view.headerTags.text, I can see it contains a string of tags, so it seems to be working. However, when testing in the app, it's not appearing in the text view.
If I set view.headerTags.text = "Tags should appear here", it works, so I'm not sure there's a problem with the text view.
Can't see to work out why it wouldn't appear, especially if the console is printing out the contents of text view? Can anyone let me know what I might be missing here?
Please use
Spannable word2 = new SpannableString("By signing in, I agree to The xxxxx\nxxxxxxx Terms of Service and Privacy Policy.");
word2.setSpan(clickableSpan, 44, 60, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
word2.setSpan(clickableSpan1, 65, 80, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(word2);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.TRANSPARENT);
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View textView) {
Intent intent = new Intent(SignInActivity.this, ReadTermsOfServiceActivity.class);
intent.putExtra("FROM", "termsservices");
startActivity(intent);
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
}
};

ClickableSpan avoid touch propagation to parent view

I have a SpannableString set as the text of my TextView which is inside another view that has a click handler. I've got ClickableSpans inside my text which are, you are right, clickable.
My problem is that I need touch events to be captured in case of a click inside the clickable span and not to propagate to parent view (as the parent has another click handler).
The container view is simply a feed of posts in my app, and those posts can contain hashtags/mentions/links etc. If a user taps a hashtag, they should go to the hashtag which is handled by the clickable span, otherwise, they should go to the post, which is handled by the container itself.
Is there any straightforward mechanism to implement this functionality?
I've came up with a terrible, anti-pattern solution that works well.
In my app class, I've defined:
public static boolean shouldIgnoreNextTouchEvent = false;
In my ClickableSpans click handler, I've set a global flag to avoid next touch event to true:
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View widget) {
App.shouldIgnoreNextTouchEvent = true;
...
}
}
Then in my parent view's handler:
#Override
public void onClick() {
if(App.shouldIgnoreNextTouchEvent){
App.shouldIgnoreNextTouchEvent = false;
return;
}
...
}
I know it's not a good solution but it works like a charm.
Alternately,
Add a tag to the widget that generated the click event
`
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View widget) {
widget.setTag("hashtag"); // or anything else
}
}
`
In the parent, check if the tag is present. If it is, this has been consumed by the ClickableSpan
`
#Override
public void onClick(View view) {
String tag = (String) view.getTag();
if(tag.equals("hashtag"){
// Already consumed by child (ClickableSpan)
return;
}
// rest of your code ...
}
`

Handle clicks on TextView's links while using Linkify for finding and setting the links inside the text

i have a TextView filled with text which i get from a server. I'm using Linkify for handling all the link searching and for setting a URLSpan where needed throughout its addLinks method.
The problem is that the default behavior when clicking a link is opening it in a browser, what i want is to get the clicked link and handle it my self.
I don't see any method of Linkify which let me set a "OnClick" or something...
Thank for your help :)
Ok so i finally managed to set my own "OnClickListener" to the TextView's links.
My solution was to copy Linkify to my project, name it CustomLinkify and just change its applyLink method:
From:
private static final void applyLink(String url, int start, int end, Spannable text)
{
URLSpan span = new URLSpan(url);
text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
To:
private static final void applyLink(final String url, int start, int end, Spannable text)
{
URLSpan span = new URLSpan(url)
{
#Override
public void onClick(View widget)
{
_onLinkClickListener.onLinkClicked(url);
}
};
text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
Where _onLinkClickListener is a new field, set by me before using the new CustomLinkify.
I know its not a very elegant solution and i prefered google to allow setting a listener through the native Linkify, but, for me, this is better than implementing my own Spannable logics (as suggested in other related questions).
I trust the Linkify code and i guess i'll check from time to time to see if any changes made on it, if so, i'll of course update CustomLinkify with the changes.
Hope this will help someone.
I also find it tedious to implement custom Spannable logics in app, and end up creating a library for this. See Textoo.
With Textoo, this can be achieve like:
TextView myTextView = Textoo
.config((TextView) findViewById(R.id.my_text_view))
.linkifyEmailAddresses()
.linkifyMapAddresses()
.linkifyPhoneNumbers()
.linkifyWebUrls() // or just .linkifyAll()
.linkify(patternSettings, "internal://settings/")
.linkify(patternGoogle, "http://www.google.ie/search2?q=", null, transformFilter)
.linkify(patternGoogle, "http://www.google.ie/search3?q=", matchFilter, transformFilter)
.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();
Just to share and hope somebody will find it useful.
Maybe you should use a WebView instead of a TextView?

Change android SpannableString link text color when pressed

How to change text color of a link when pressed(Touch or pressed effect). Please see my code below
Code:
SpannableString spanStr = new SpannableString(tag);
spanStr.setSpan(new HashTagsClickableSpan(tag), 0, tag.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanStr.setSpan(new ForegroundColorSpan(Color.RED),0,tag.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
class HashTagsClickableSpan extends ClickableSpan{
String clicked;
public HashTagsClickableSpan(String string) {
super();
clicked =string;
}
public void onClick(View tv) {
Toast.makeText(getActivity(), "Text = " + clicked,Toast.LENGTH_SHORT).show();
}
#Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(false);
}
}
ClickableSpan.onClick is only called after the ACTION_UP, so it's probably too late for the effect you want. In general, you can call View.invalidate() to issue a redraw (and another call to updateDrawState).
To get a nice pressed effect, you're probably better off registering a touch listener with View.setOnTouchListener , listening for the mouse events directly, modifying your custom span state if necessary, and posting an invalidate.

Format TextView to look like link

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

Categories

Resources