I need to push text into WebView. But there are a lot of different HTML tags in this text and I want to parse it before.
Spanned html = Html.fromHtml(texts.get(i));
But I need to change URLs in the text to call one function in my activity. In TextView I may do it like that:
ClickableSpan[] items = spans.getSpans(0, spans.length(), ClickableSpan.class);
for (ClickableSpan s : items) {
final String url = ((URLSpan)s).getURL();
int spanStart = spans.getSpanStart(s);
int spanEnd = spans.getSpanEnd(s);
spans.removeSpan(s);
spans.setSpan(new URLSpan(url) {
#Override
public void onClick(View widget) {
//code for overriding onClick goes here
}
}, spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
Is it possible to use Html.fromHTML() AND to change links in text to call my function when using WebView instead of TextView?
Not really, because WebView understands HTML, not a Spanned. You are welcome to try to convert the Spanned back into HTML via Html.toHtml(), but the round-trip conversion from HTML to Spanned to HTML is likely to make a hash of any complex formatting, because they are not complete HTML parsers or generators.
I would recommend that you find and use some Java library that is designed to parse and help you fix up HTML.
Related
I am working on chat project. For showing chat messages I am using the following layout
<TextView
android:id="#+id/messageText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:longClickable="true"
android:textColor="#000000"
tools:text="hello hello hello hello" />
And on java side, I am simply formatted the message text and then set it to the TextView.
I am facing 2 main issues:
When I click on weblink in the message, screen scrolls either top or bottom. This does not happen when I click the non-weblink portion of the message.
Click doesn't navigate to the corresponding webpage. With too much difficulty, sometimes it happens that I am able to navigate to the webpage.
Things I have tried:
I tried using the setMovementMethod(context) method after setting the text but didn't work.
I also tried things like removing the autoLink="web" from layout as suggested by some.
I also tried setLinksClickable(true) and setAutoMask(0) but nothing is working.
I have spent quite some time on it now. Can someone help me here.
Thanks in advance.
you must use an inherited class from ClickableSpan, as such setLinksClickable TextView's method is not enough, each link in the text assigned to TextView must have the click event.
The solution is coded in Xamarin, but i thinks is easily translatable to Java.
With this function, we check de html string text, and we get the links and make each of them clickable with MakeLinkClickable function.
public static void SetTextAsHtmlWithOverrideLinks(Android.Widget.TextView textview, string text)
{
var sequence = text.GetTextAsHtml();
var strBuilder = new Android.Text.SpannableStringBuilder(sequence);
var urls = strBuilder.GetSpans(0, sequence.Length(), Java.Lang.Class.FromType(typeof(Android.Text.Style.URLSpan)));
foreach (Android.Text.Style.URLSpan span in urls) {
MakeLinkClickable(strBuilder, span);
}
textview.MovementMethod = Android.Text.Method.LinkMovementMethod.Instance;
textview.TextFormatted = strBuilder;
}
This function makes each link clickable with LinkSpan class, which has our action on link click
public static void MakeLinkClickable(Android.Text.SpannableStringBuilder strBuilder, Android.Text.Style.URLSpan span)
{
strBuilder.SetSpan(new LinkSpan(span), strBuilder.GetSpanStart(span), strBuilder.GetSpanEnd(span),strBuilder.GetSpanFlags(span));
strBuilder.RemoveSpan(span);
}
public class LinkSpan : Android.Text.Style.ClickableSpan
{
public Android.Text.Style.URLSpan Link { get; }
public LinkSpan(Android.Text.Style.URLSpan span) {
Link = span;
}
public override void OnClick(Android.Views.View widget)
{
if (widget is Android.Widget.TextView textView)
// Here open url or another action with your own method;
widget.Invalidate();
}
}
This function converts our html text to a spannable text to handle it.
public static Android.Text.ISpanned GetTextAsHtml(this string input)
{
if (input == null)
input = string.Empty;
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.N)
return Android.Text.Html.FromHtml(input,Android.Text.FromHtmlOptions.ModeLegacy);
else
return Android.Text.Html.FromHtml(input);
}
Use:
SetTextAsHtmlWithOverrideLinks(yourtextView, theHTMLTextString);
I am trying to use this library for my emojicons https://github.com/ankushsachdeva/emojicon.
I have implemented it with my soft keyboard but it's not doing anything as in the code (MainActivity) of the given link, it uses EmojiconEditText instead of normal text view of any android app. Is there any way so that I can use edit text of any app instead of this custom one. My current source code for emoji is this.
popup = new EmojiconsPopup(mInputView, this);
popup.setOnEmojiconClickedListener(new EmojiconGridView.OnEmojiconClickedListener() {
#Override
public void onEmojiconClicked(Emojicon emojicon) {
if (emojicon == null){
return;
}
emojicon.getEmoji();
}
});
In this, I cannot figure out how to fetch Unicode for that particular emoji. If someone can help me how do I fetch emoji Unicode?
The code for emoji Unicode is in this https://github.com/ankushsachdeva/emojicon/tree/master/lib/src/github/ankushsachdeva/emojicon/emoji
I have got the answer after some struggle. I just need to call that string of unicodes in it with current input connection.
String text = emojicon.getEmoji();
mComposing.append(text);
commitTyped(getCurrentInputConnection());
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?
I have been attempting to implement a custom SoftKeyboard (IME) which contains and outputs icons for keys. Think emoji. I have skinned the keys, but injecting the selected drawable into the String Builder has proven difficult. I decided to implement my own ImageGetter so that I could control what was returned by Html.fromHtml(). The fromHtml method continues to act as if it's returning null even though I know that the drawable is not null.
Any help would be greatly appreciated.
I based this project off of the SoftKeyboard sample. I targeted the file, SoftKeyboard.java an instance of
public class SoftKeyboard extends InputMethodService
implements KeyboardView.OnKeyboardActionListener
This class instantiates an object mComposing, this is used to store the text entered into the edit box from the soft keyboard.
private StringBuilder mComposing = new StringBuilder();
I thought that I could append the images as a Spanned to the StringBuilder and display the image that way. What happens here is that a white square with "obj" text in it shows instead of the selected icon, meaning getDrawable() is returning null.
I have tried a few things with the bounds but I am not sure if I am close or if I am going down the wrong path.
Declaring my drawable in onCreate:
mHangingDrawable = getResources().getDrawable(R.drawable.hanging_tiny);
mHangingDrawable.setBounds(0, 0, mHangingDrawable.getIntrinsicWidth(), mHangingDrawable.getIntrinsicHeight());
In handleCharacter(int, int[]) I wrote this catch for codes that I have personally mapped.
else if (primaryCode == 50) {
String htmlSource = "<img src='hanging_small' />";
ImageGetter ig = new ImageGetter(){
#Override
public Drawable getDrawable(String source) {
return mHangingDrawable;
}
};
Spanned htmlSpan = Html.fromHtml(htmlSource, ig, null);
mComposing.append(htmlSpan);
getCurrentInputConnection().commitText(mComposing, 1);
}
This article about Creating Input Methods was a helpful resource.
As the title explains, I'd like to add links to my TextView, with these two caveats:
I want the link to act on a part of the TextView, not the full one (something like an A anchor in HTML).
I want the link to point to an action in my code, not a website. I could define a method in my activity, or implement an OnClickListener, and execute that when that specific link is clicked.
So far, I succeeded to turn phone numbers, addresses, web sites and emails into dedicated external links using:
Linkify.addLinks(message, Linkify.ALL);
I'd like something similar for internal links (to my method), with the possibility to define custom ones.
Also, using a web page with internal link and a web view is not really an option, as I already have several complex layouts defined, and having to modify the whole application and concepts would be quite a pain...
Any idea?
EDIT: Kabuko gave me a very good solution, here is exactly how I implemented it:
final TextView descriptionTextView = (TextView) findViewById(R.id.description);
final Spannable span = Spannable.Factory.getInstance().newSpannable("the full text for the view");
span.setSpan(new ClickableSpan() {
#Override
public void onClick(View widget) {
Toast.makeText(StartEventActivity.this, "LINK CLICKED", Toast.LENGTH_SHORT).show();
}
}, 1, 20, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // 1 and 20 to be replaced with actual index of start and end of the desired link
descriptionTextView.setText(span);
descriptionTextView.setMovementMethod(LinkMovementMethod.getInstance());
If you wanted to actually go to URLs you could use Html.fromHtml, but if you want your own click handlers you can use a ClickableSpan.
In completion of previous post this might help some one
TextView textView = (TextView) getView().findViewById(R.id.textView);
textView.setText(Html.fromHtml(getString(R.string.html_)) , TextView.BufferType.SPANNABLE);
String tmp = ((Spannable) textView.getText()).toString();
String linkText = getString(R.string.html_link);
int index = tmp.indexOf(linkText);
if(index>=0) {
Spannable spannable = (Spannable) textView.getText() ;
spannable.setSpan(new ClickableSpan() {
#Override
public void onClick(View widget) {
try {
/// do what you must
}catch (Exception ex){
handleException(ex);
}
}
}, index, index+getString(R.string.html_link).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannable);
textView.setMovementMethod(LinkMovementMethod.getInstance());
}