textview with multiple hyperlinks - android

I have for example the following String
\n 3 Doors Down is a http://www.last.fm/tag/post-grunge\" class=\"bbcode_tag\" rel=\"tag\">post-grunge band from Escatawpa, Mississippi, United States, formed in 1996, consisting of Brad Arnold (vocals), Matt Roberts (guitars), Todd Harrell (bass), Chris Henderson (guitar), and Greg Upchurch (drums). The band signed to Universal Records in 2000 for their first album, http://www.last.fm/music/3+Doors+Down/The+Better+Life\" class=\"bbcode_album\">The Better Life. They received international attention with the release of the single "http://www.last.fm/music/3+Doors+Down/_/Kryptonite\" class=\"bbcode_track\">Kryptonite". The album went on to sell over 6 million copies. \n\n http://www.last.fm/music/3+Doors+Down\">Read more about 3 Doors Down on Last.fm.\n \n \nUser-contributed text is available under the Creative Commons By-SA License and may also be available under the GNU FDL.\n
I want to show the entire string in a textview with the hyperlinks clickable. I also don't want to see the actual url, just the text that's shown in place of the url. Reading other posts on the subject they all suggest defining a textview similar to this
<TextView
android:id="#+id/tvArtistOverview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:autoLink="web"
android:linksClickable="true" />
and setting the SetMovementMethod of the textview to
myTextView.setMovementMethod(LinkMovementMethod.getInstance());
When I follow these steps, my links are clickable, however they are not displayed as I wish. What am I missing?
Here is an example of how it currently looks.

Use below code.
TextView tv = ....
tv.setMovementMethod(LinkMovementMethod.getInstance());
String content = tv.getText().toString();
List<String> links = new ArrayList<String>();
Pattern p = Patterns.WEB_URL;
Matcher m = p.matcher(content);
while (m.find()) {
String urlStr = m.group();
links.add(urlStr);
}
SpannableString f = new SpannableString(content);
for (int i = 0; i < links.size(); i++) {
final String url = links.get(i);
f.setSpan(new InternalURLSpan(new OnClickListener() {
public void onClick(View v) {
Context ctx = v.getContext();
String urlToOpen = url;
if (!urlToOpen.startsWith("http://") || !urlToOpen.startsWith("https://"))
urlToOpen = "http://" + urlToOpen;
openURLInBrowser(urlToOpen, ctx);
}
}), content.indexOf(url), content.indexOf(url) + url.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
tv.setText(f);

Related

Inputting Array In Multiline Text In Android

I am new to android and am trying to make an app where the user inputs an array in a text box with inputType = textMultiLine. The problem is that I want to make it so that whenever user hits enter, the app takes input of the next array element and not treat the entire text in the textbox as one element. The code is as below :
EditText input = findViewById(R.id.inputtext);
Button show = findViewById(R.id.button);
TextView output = findViewById(R.id.output);
String [] name = new String[3];
for (int i = 0 ; i < 3 ; i++)
{
name[i] = input.getText().toString();
output.setText(name[i]);
}
But whenever i try to take name[1] after hitting enter the app doesnt treat the next line as name[2] but instead treats it as name[1]. For example if type the names john,steve and frank, then i should get an array that is like this :
name[0] = john
name[1] = steve
name[2] = frank
but instead whenever I typejohn,press enter,type steve, press enter and type frank the app treats it as :
name[0] = john
steve
frank
also if i set the output to something like this :
output.setText(name[i] + i)
instead of getting an oupt like this :
john 0
steve 1
frank 2
I get an output like this :
john
steve
frank2
Any and all help would be much appreciated.
Thanks
======================================================================================================================================================
EDIT 1
I tried this code but didn't work:
String name[] = input .getText().toString().split("\\r?\\n");
for (int i = 0 ; i < name.length; i++)
{
output.setText(name[i]);
}
Still get only frank when I input john,steve and frank
If you want to put each line to different array item :
String [] name = input.getText().toString().split("\n");
input.getText().toString() gives you string containing whole EditText content with lines separated by new line - "\n". You need to split this string to get each line.
try below code
String name[] = input .getText().toString().split("\\r?\\n");
String disp="";
for (int i = 0 ; i < name.length; i++)
{
disp += name[i] +"\n";
}
output.setText(disp);
Maybe the following example will be useful:
XML file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<EditText
android:id="#+id/edit_text"
style="#style/Widget.AppCompat.EditText"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginBottom="100dp"
android:inputType="textMultiLine"/>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show"/>
<TextView
android:id="#+id/text_view"
style="#style/Widget.AppCompat.EditText"
android:layout_width="300dp"
android:layout_height="wrap_content"/>
</LinearLayout>
Java Code
final EditText input = findViewById(R.id.edit_text);
final Button show = findViewById(R.id.button);
final TextView output = findViewById(R.id.text_view);
show.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View view) {
final String inputString = input.getText().toString();
if (!TextUtils.isEmpty(inputString)) {
final String newLine = System.getProperty("line.separator");
final String[] inputText = inputString.split(newLine);
String outputText = "";
for (int i = 0; i < inputText.length; i++) {
outputText += inputText[i];
if (i != inputText.length - 1) {
outputText += newLine;
}
}
output.setText(outputText);
}
}
});
}
You can download de APK here or here the complete source code
(another way:simple!) In the following code it is not necessary to make a split on the input text.
final EditText input = findViewById(R.id.edit_text);
final Button show = findViewById(R.id.button);
final TextView output = findViewById(R.id.text_view);
show.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View view) {
final String inputString = input.getText().toString();
if (!TextUtils.isEmpty(inputString)) {
output.setText(inputString);
}
}
});
Note that, your code is wrong, because in for each loop, you override the text was setted in the previous loop.

Getting values of EditTexts from multiple Layouts in Android not working?

Not asked a question in a while so it's been long overdue!
I am creating an app where job items can be created onClick, with each new row containing a Description(EditText), a Price(EditText) and a button to delete the current row, but I am having trouble when getting the values from the EditText fields when there is more than one row - it just returns the values of the newest row.
Aside from the 'Job List Container', the views are created dynamically so pardon the lack of XML, but the structure of what I am trying to achieve is as follows, where clicking the Add button adds a row (this can be multiple rows) and clicking the submit button takes all of the Description and Price values and processes them (adds the prices and adds the job to the DB):
...and this is the code I've written for it called from the addNewJobRow onClick listener (all together for simplicity):
private void addJobItem() {
//Create a new row container
final LinearLayout jobRowContainer = new LinearLayout(this);
//Create a new EditText for the Description
final EditText description = new EditText(this);
description.setHint("Description...");
description.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
1.0f
));
//Create an EditText for the Price
final EditText price = new EditText(this);
price.setHint("00.00");
//Create a new button to delete the row
Button delete = new Button(this);
delete.setBackgroundColor(Color.RED);
delete.setText("X");
//Add Description, Price and Delete to the row container
jobRowContainer.addView(description);
jobRowContainer.addView(price);
jobRowContainer.addView(delete);
//Add the Row Container to the Jobs List Container
ll_jobListContainer.addView(jobRowContainer);
//Get the values of the Description and Price, for each row
btn_JobSubmit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i = 0; i < ll_jobListContainer.getChildCount(); i++) {
for(int j = 0; j < jobRowContainer.getChildCount(); j++) {
if (jobRowContainer.getChildAt(i) instanceof EditText){
String descriptionString = description.getText().toString();
String priceString = price.getText().toString();
System.out.println("z! " + descriptionString + " # " + priceString);
}
}
}
}
});
}
I have tried a couple of iterations of this with and without the nested FOR loops and with and without the use of instanceof, but all it does is print out the newest row.
So, if I have multiple job rows, how can I get all of the values as required?
Thanks for your time and all that nice stuff xxx
The basic problem is that you're using only the last instance of description and price instead of each rows instance. (This may be what Dmitry is saying as well). To fix it, you need to get the input for each row. Here's one way.
Set an ID for description & price. (You can't just use '1' or '2', it needs to be a resource type ID so it is guaranteed to be unique). I made a dummy layout file of a row & assigned IDs in that to the 2 EditTexts. There may be a better way to do it. So anyway, add these 2 lines in your declarations
descripton.setId(R.id.description); and price.setId(R.id.price);
Now this is your onClick()
public void onClick(View v) {
for (int i = 0; i < ll_jobListContainer.getChildCount(); i++) {
LinearLayout currentRow = (LinearLayout)ll_jobListContainer.getChildAt(i);
EditText editText = (EditText)currentRow.findViewById(R.id.description);
String descriptionString = editText.getText().toString();
editText = (EditText)currentRow.findViewById(R.id.price);
String priceString = editText.getText().toString();
Log.d(TAG, "z! " + descriptionString + " # " + priceString);
}
}
EDIT: I didn't want to change this answer since it had already been accepted so I've put a more concise solution in another answer.
Of cause, your last setOnClickListener takes strings
String descriptionString = description.getText().toString();
String priceString = price.getText().toString();
Where description and price - is fields in the function (last edittexts).
The good way to do that is to use RecyclerView/ListView, in "onTextChangeListner" of ViewHolder save new text to model of this object and print all text from your models, not directly from views.
I normally try to answer only question that was asked rather than change code that's not necessary. However, in this case, since I had created a dummy layout just to get Resource IDs, I wonder if that layout file could be put to use. I had started to change my answer but original one was accepted before I could make the changes. I've put a different version of the solution here. I didn't want to modify an answer that had already been accepted.
private void addJobItem() {
//Create a new row container
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout jobRowContainer = (LinearLayout)inflater.inflate(R.layout.row_layout, null);
//Add the Row Container to the Jobs List Container
ll_jobListContainer.addView(jobRowContainer);
//Get the values of the Description and Price, for each row
btn_JobSubmit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i = 0; i < ll_jobListContainer.getChildCount(); i++) {
LinearLayout currentRow = (LinearLayout)ll_jobListContainer.getChildAt(i);
EditText editText = (EditText)currentRow.findViewById(R.id.description);
String descriptionString = editText.getText().toString();
editText = (EditText)currentRow.findViewById(R.id.price);
String priceString = editText.getText().toString();
Log.d(TAG, "z! " + descriptionString + " # " + priceString);
}
}
});
}
row_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/single_row">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Description..."
android:id="#+id/description"
/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="00.00"
android:id="#+id/price"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/holo_red_dark"
android:text="X"
android:id="#+id/clear_button"
/>
</LinearLayout>

Obscure issue with string formatting from SQLite database

I appreciate there's a lot of helpful stack questions and answers to my question but I'm running into problems I've not had in the past.
The Problem:
I am using a cursor to populate textviews in rows on a view (without using listview - that's crazy I know). I am trying to format the string value(s) taken from from the database column STUDENT_POINTS that are put into a textview tpoints. Here is the code I am using:
public void bindView(View v, final Context context, Cursor c) {
final int id = c.getInt(c.getColumnIndex(Students.STUDENT_ID));
final String name = c.getString(c.getColumnIndex(Students.STUDENT_NAME));
final String age = c.getString(c.getColumnIndex(Students.STUDENT_AGE));
final String points = c.getString(c.getColumnIndex(Students.STUDENT_POINTS));
final String teachernote = c.getString(c.getColumnIndex(Students.TEACHERNOTE));
final byte[] image = c.getBlob(c.getColumnIndex(Students.IMAGE));
ImageView iv = (ImageView) v.findViewById(R.id.photo);
if (image != null) {
if (image.length > 3) {
iv.setImageBitmap(BitmapFactory.decodeByteArray(image, 0,image.length));
}
}
TextView tname = (TextView) v.findViewById(R.id.name);
tname.setText(name);
TextView tage = (TextView) v.findViewById(R.id.age);
tage.setText(age);
TextView tpoints = (TextView) v.findViewById(R.id.points);
tpoints.setText(String.format(points, "%1$,.2f"));
final StudentsConnector sqlCon = new StudentsConnector(context);
The rest of bindView is for buttons so I have not included it here. The problem is with the line:
tpoints.setText(String.format(points, "%1$,.2f"));
I'm intending to have commas to separate out large numbers but this does nothing! If anybody has the time could you please tell me what I'm doing wrong?
Thanks in advance.
You have your two parameters backwards-- you should have the format string followed by the data string: String.format("%1$,.2f", points );
This formatted nicely for me with this little snippet in my code:
double points = 56789.45f;
String boogie = String.format("%1$,.2f", points );
and it generated a number 56,789.45
But bigger numbers don't work well due to precision in the formater. You may want to split the mantissa off of their, format them separately and combine them.

Anything better than JSoup for Android?

What I want to do...
I have a webview in my android app. I get a huge html content from the server as a string and a search string from the application user(the android phone user). Now I break the search string and create a regex out of it. I want all the html content that matches my regex to be highlighted when I display it into my WebView.
What I tried...
Since it is html, I just want to wrap the regex matched words into a pair of tags with yellow background.
Simple regex and replaceAll on the html Content that i get. Very wrong because it screws and replaces even what is inside the '<' and '>'.
I tried using Matcher and Pattern combo. It is difficult to omit what is inside the tags.
I used JSOUP Parser and it worked!
I traverse the html using NodeTraversor class. I used Matcher and Pattern classes to find and replace matched words with tags as i wanted to do.
But it is very slow. And I basically want to use it on Android and the size of it is like 284kB. I removed some unwanted classes and it is now 201kB but it is still too much for an android device. Additionally, the html content can be really large. I looked into JSoup source as well. It kind of iterates over every single character when it parses. I do not know whether all the parsers do the same but it is definitely slow for large html documents.
Here is my code -
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Highlighter {
private String regex;
private String htmlContent;
Pattern pat;
Matcher mat;
public Highlighter(String searchString, String htmlString) {
regex = buildRegexFromQuery(searchString);
htmlContent = htmlString;
pat = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
}
public String getHighlightedHtml() {
Document doc = Jsoup.parse(htmlContent);
final List<TextNode> nodesToChange = new ArrayList<TextNode>();
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
#Override
public void tail(Node node, int depth) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getWholeText();
mat = pat.matcher(text);
if(mat.find()) {
nodesToChange.add(textNode);
}
}
}
#Override
public void head(Node node, int depth) {
}
});
nd.traverse(doc.body());
for (TextNode textNode : nodesToChange) {
Node newNode = buildElementForText(textNode);
textNode.replaceWith(newNode);
}
return doc.toString();
}
private static String buildRegexFromQuery(String queryString) {
String regex = "";
String queryToConvert = queryString;
/* Clean up query */
queryToConvert = queryToConvert.replaceAll("[\\p{Punct}]*", " ");
queryToConvert = queryToConvert.replaceAll("[\\s]*", " ");
String[] regexArray = queryString.split(" ");
regex = "(";
for(int i = 0; i < regexArray.length - 1; i++) {
String item = regexArray[i];
regex += "(\\b)" + item + "(\\b)|";
}
regex += "(\\b)" + regexArray[regexArray.length - 1] + "[a-zA-Z0-9]*?(\\b))";
return regex;
}
private Node buildElementForText(TextNode textNode) {
String text = textNode.getWholeText().trim();
ArrayList<MatchedWord> matchedWordSet = new ArrayList<MatchedWord>();
mat = pat.matcher(text);
while(mat.find()) {
matchedWordSet.add(new MatchedWord(mat.start(), mat.end()));
}
StringBuffer newText = new StringBuffer(text);
for(int i = matchedWordSet.size() - 1; i >= 0; i-- ) {
String wordToReplace = newText.substring(matchedWordSet.get(i).start, matchedWordSet.get(i).end);
wordToReplace = "<b>" + wordToReplace+ "</b>";
newText = newText.replace(matchedWordSet.get(i).start, matchedWordSet.get(i).end, wordToReplace);
}
return new DataNode(newText.toString(), textNode.baseUri());
}
class MatchedWord {
public int start;
public int end;
public MatchedWord(int start, int end) {
this.start = start;
this.end = end;
}
}
}
Here is how I call it -
htmlString = getHtmlFromServer();
Highlighter hl = new Highlighter("Hello World!", htmlString);
new htmlString = hl.getHighlightedHTML();
I am sure what i'm doing is not the most optimal way. But I can't seem to think of anything else.
I want to
- reduce the time it takes to highlight it.
- reduce the size of library
Any suggestions?
How about highlighting them using javascript?
You know, everybody love javascript, and you can find example like this blog.
JTidy and HTMLCleaner are aloso among the best Java HTML Parser.
see
Comparison between different Java HTML Parser
and
What are the pros and cons of the leading Java HTML parsers?

Is there a way to italicize certain words when displaing it in a TextView on Android?

My Android app is displaying text in a TextView.
Are there any tags or anything to put around words that I want italicized? I don't need to set the TextView as italics because the whole sentence would be that way, and I only need specific words italicized.
You need to use a Spannable: see Is there any example about Spanned and Spannable text for an example.
I agree, this isn't a database issue. If this is not a custom app you're working on, you're out of luck. If it is, save the field in the database as HTML.
This function sets a string as the text of a TextView and italicizes one or more spans within that string as long as the span or spans are marked with the tags [i] and [/i]. Of course, it can be modified for other types of styling.
private void doItalicize(TextView xTextView, String xString) {
ArrayList<Integer> IndexStart = new ArrayList<>();
ArrayList<Integer> IndexEnd = new ArrayList<>();
ArrayList<StyleSpan> SpanArray = new ArrayList<>();
int i = 0;
do {
IndexStart.add(i, xString.indexOf("[i]"));
IndexEnd.add(i, xString.indexOf("[/i]") - 3);
xString = xString.replaceFirst("\\[i\\]", "");
xString = xString.replaceFirst("\\[/i\\]", "");
xTextView.setText(xString, TextView.BufferType.SPANNABLE);
SpanArray.add(i, new StyleSpan(Typeface.ITALIC));
Log.d(LOG_TAG, "i: " + i);
i++;
} while (xString.contains("[i]"));
Spannable xSpannable = (Spannable) xTextView.getText();
for (int j = 0; j < i; j++)
xSpannable.setSpan(SpanArray.get(j), IndexStart.get(j), IndexEnd.get(j), Spanned
.SPAN_EXCLUSIVE_EXCLUSIVE);
}
The solution would seem to be to save the content in the database as HTML and then output the HTML in the textview.
See the following article: How to display HTML in TextView?

Categories

Resources