I have set html data to my textview. When I select any word/characters from textview I want to bold that word and replace original html data with new one.
String html = "<p>hello this is android testing</p>";
Like this my html maybe have many html tags in it. If I want to make "android" word bold, how can I replace original html string with "android".
I want <p>hello this is <strong>android</strong> testing</p> as result.
You can first set you string content into the TextView and then use setCustomSelectionActionModeCallback(...) to intercept the selection within the TextView.
In the example bellow a selected word will be surrounded by <strong>...</strong>.
For example selecting "testing" will make the following string visible into the TextView.
hello this is android testing android bla bla android bla bla android bla
Then selecting the last instance on "android" in the already transformed TextView content will make the following string visible into the TextVIew.
hello this is android testing android bla bla android bla bla android bla
Code :
public class MainActivity extends AppCompatActivity {
String yourString = "<p>hello this is android testing android bla bla android bla bla android bla</p>";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.opening_today);
tv.setText(Html.fromHtml(yourString));
tv.setCustomSelectionActionModeCallback(new CallbackListener(tv));
}
class CallbackListener implements ActionMode.Callback{
private TextView tv;
private String strongS = "<strong>";
private String strongE = "</strong>";
public CallbackListener(TextView tv) {
this.tv = tv;
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
int start = tv.getSelectionStart();
int end = tv.getSelectionEnd();
if( start == 0 && end == 0)
return false;
String content = tv.getText().toString();
String token = content.substring(start, end);
String before = content.substring(0, start);
String after = content.substring(end, content.length());
content = makeItStrong(before, after, token);
tv.setText(Html.fromHtml(content));
return false;
}
private String makeItStrong(String before, String after, String token){
return cleanTags(before, strongS, strongE) + strongS + token + strongE + cleanTags(after, strongS, strongE);
}
private String cleanTags(String source, String... exp){
for(String s: exp){
source = source.replace(s, "");
}
return source;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return true; }
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; }
#Override
public void onDestroyActionMode(ActionMode mode) {}
}
}
Use SelectableTextView to get the selected text. You can see this implementation on github here.
Check the answer here. This might help you
And you can make bold using tag,
String string= "<b>" + android + "</b> " + remainingString;
textView.setText(Html.fromHtml(string));
With the following code, you can highlight formatted text more than once and each time you can get the cumulative result of all highlights.
int getOccuranceNumber(String text ,String selection, int selectionStartIndex){
int index = text.indexOf(selection);
int occuranceNumber = 0;
while (index >= 0) {
if(index == selectionStartIndex){
return occuranceNumber;
}else {
index = text.indexOf(selection, index + 1);
occuranceNumber++;
}
}
return occuranceNumber;
}
int getOccuranceIndex(String text ,String selection, int occuranceNumber){
int index = text.indexOf(selection);
int i = 0;
while (i!=occuranceNumber) {
index = text.indexOf(selection, index + 1);
i++;
}
return index;
}
public String toHex(String arg) {
TextView textV = new TextView(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textV.setText(Html.fromHtml(arg,Html.FROM_HTML_MODE_LEGACY));
}else{
textV.setText(Html.fromHtml(arg));
}
String textFormated ="";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textFormated = Html.toHtml((Spanned) textV.getText(),TO_HTML_PARAGRAPH_LINES_CONSECUTIVE).toString();
}else{
textFormated = Html.toHtml((Spanned) textV.getText()).toString();
}
return textFormated.replace("<p dir=\"rtl\">","").replace("</p>","").replace("\n","");
}
public void highLightText(TextView textView){
String textFormated ="";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textFormated = Html.toHtml((Spanned) textView.getText(),TO_HTML_PARAGRAPH_LINES_CONSECUTIVE).toString();
}else{
textFormated = Html.toHtml((Spanned) textView.getText()).toString();
}
String text = textView.getText().toString();
String selection = text.substring(textView.getSelectionStart(),textView.getSelectionEnd());
int occuranceNum = getOccuranceNumber(text,selection,textView.getSelectionStart());
String selectionHex = toHex(selection);
int formatedTextSelectionStart = getOccuranceIndex(textFormated,selectionHex,occuranceNum);
if(formatedTextSelectionStart<0)return;
int formatedTextSelectionEnd = formatedTextSelectionStart + selectionHex.length();
String formatedTextPart1 = textFormated.substring(0,formatedTextSelectionStart);
String formatedTextPart2 = textFormated.substring(formatedTextSelectionStart,formatedTextSelectionEnd);
String formatedTextPart3 = textFormated.substring(formatedTextSelectionEnd,textFormated.length());
String colorOpenTag="<font color=\"#f7c627\">";
String colorCloseTag="</font>";
String finalText = formatedTextPart1+colorOpenTag+formatedTextPart2+colorCloseTag+formatedTextPart3;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textView.setText(Html.fromHtml(finalText,Html.FROM_HTML_MODE_LEGACY));
}else{
textView.setText(Html.fromHtml(finalText));
}
}
Related
I want to show my numbers in money format and separate digits like the example below:
1000 -----> 1,000
10000 -----> 10,000
100000 -----> 100,000
1000000 -----> 1,000,000
Thanks
Another approach :
NumberFormat format = NumberFormat.getCurrencyInstance();
format.setMaximumFractionDigits(0);
format.setCurrency(Currency.getInstance("EUR"));
format.format(1000000);
This way, it's displaying 1 000 000 € or 1,000,000 €, depending on device currency's display settings
You need to use a number formatter, like so:
NumberFormat formatter = new DecimalFormat("#,###");
double myNumber = 1000000;
String formattedNumber = formatter.format(myNumber);
//formattedNumber is equal to 1,000,000
Hope this helps!
double number = 1000000000.0;
String COUNTRY = "US";
String LANGUAGE = "en";
String str = NumberFormat.getCurrencyInstance(new Locale(LANGUAGE, COUNTRY)).format(number);
//str = $1,000,000,000.00
Currency formatter.
public static String currencyFormat(String amount) {
DecimalFormat formatter = new DecimalFormat("###,###,##0.00");
return formatter.format(Double.parseDouble(amount));
}
Use this:
int number = 1000000000;
String str = NumberFormat.getNumberInstance(Locale.US).format(number);
//str = 1,000,000,000
This Method gives you the exact output which you need:
public String currencyFormatter(String num) {
double m = Double.parseDouble(num);
DecimalFormat formatter = new DecimalFormat("###,###,###");
return formatter.format(m);
}
Try the following solution:
NumberFormat format = NumberFormat.getCurrencyInstance();
((TextView)findViewById(R.id.text_result)).setText(format.format(result));
The class will return a formatter for the device default currency.
You can refer to this link for more information:
https://developer.android.com/reference/java/text/NumberFormat.html
Here's a kotlin Extension that converts a Double to a Currency(Nigerian Naira)
fun Double.toRidePrice():String{
val format: NumberFormat = NumberFormat.getCurrencyInstance()
format.maximumFractionDigits = 0
format.currency = Currency.getInstance("NGN")
return format.format(this.roundToInt())
}
Use a Formatter class
For eg:
String s = (String.format("%,d", 1000000)).replace(',', ' ');
Look into:
http://developer.android.com/reference/java/util/Formatter.html
The way that I do this in our app is this:
amount.addTextChangedListener(new CurrencyTextWatcher(amount));
And the CurrencyTextWatcher is this:
public class CurrencyTextWatcher implements TextWatcher {
private EditText ed;
private String lastText;
private boolean bDel = false;
private boolean bInsert = false;
private int pos;
public CurrencyTextWatcher(EditText ed) {
this.ed = ed;
}
public static String getStringWithSeparator(long value) {
DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
String f = formatter.format(value);
return f;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
bDel = false;
bInsert = false;
if (before == 1 && count == 0) {
bDel = true;
pos = start;
} else if (before == 0 && count == 1) {
bInsert = true;
pos = start;
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
lastText = s.toString();
}
#Override
public void afterTextChanged(Editable s) {
ed.removeTextChangedListener(this);
StringBuilder sb = new StringBuilder();
String text = s.toString();
for (int i = 0; i < text.length(); i++) {
if ((text.charAt(i) >= 0x30 && text.charAt(i) <= 0x39) || text.charAt(i) == '.' || text.charAt(i) == ',')
sb.append(text.charAt(i));
}
if (!sb.toString().equals(s.toString())) {
bDel = bInsert = false;
}
String newText = getFormattedString(sb.toString());
s.clear();
s.append(newText);
ed.addTextChangedListener(this);
if (bDel) {
int idx = pos;
if (lastText.length() - 1 > newText.length())
idx--; // if one , is removed
if (idx < 0)
idx = 0;
ed.setSelection(idx);
} else if (bInsert) {
int idx = pos + 1;
if (lastText.length() + 1 < newText.length())
idx++; // if one , is added
if (idx > newText.length())
idx = newText.length();
ed.setSelection(idx);
}
}
private String getFormattedString(String text) {
String res = "";
try {
String temp = text.replace(",", "");
long part1;
String part2 = "";
int dotIndex = temp.indexOf(".");
if (dotIndex >= 0) {
part1 = Long.parseLong(temp.substring(0, dotIndex));
if (dotIndex + 1 <= temp.length()) {
part2 = temp.substring(dotIndex + 1).trim().replace(".", "").replace(",", "");
}
} else
part1 = Long.parseLong(temp);
res = getStringWithSeparator(part1);
if (part2.length() > 0)
res += "." + part2;
else if (dotIndex >= 0)
res += ".";
} catch (Exception ex) {
ex.printStackTrace();
}
return res;
}
Now if you add this watcher to your EditText, as soon as user enter his number, the watcher decides whether it needs separator or not.
i used this code for my project and it works:
EditText edt_account_amount = findViewById(R.id.edt_account_amount);
edt_account_amount.addTextChangedListener(new DigitFormatWatcher(edt_account_amount));
and defined class:
public class NDigitCardFormatWatcher implements TextWatcher {
EditText et_filed;
String processed = "";
public NDigitCardFormatWatcher(EditText et_filed) {
this.et_filed = et_filed;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable editable) {
String initial = editable.toString();
if (et_filed == null) return;
if (initial.isEmpty()) return;
String cleanString = initial.replace(",", "");
NumberFormat formatter = new DecimalFormat("#,###");
double myNumber = new Double(cleanString);
processed = formatter.format(myNumber);
//Remove the listener
et_filed.removeTextChangedListener(this);
//Assign processed text
et_filed.setText(processed);
try {
et_filed.setSelection(processed.length());
} catch (Exception e) {
// TODO: handle exception
}
//Give back the listener
et_filed.addTextChangedListener(this);
}
}
Updated 2022 answer
Try this snippet. It formats a number in string complete with the currency & setting fractional digits.
Upvote if this helped you! :)
/**
* Formats amount in string to human-readable amount (separated with commas
* & prepends currency symbol)
*
* #param amount The amount to format in String
* #return The formatted amount complete with separators & currency symbol added
*/
public static String formatCurrency(String amount) {
String formattedAmount = amount;
try {
if (amount == null || amount.isEmpty())
throw new Exception("Amount is null/empty");
Double amountInDouble = Double.parseDouble(amount);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(new Locale("en", "IN"));
numberFormat.setMaximumFractionDigits(2);
numberFormat.setMinimumFractionDigits(2);
formattedAmount = numberFormat.format(amountInDouble);
} catch (Exception exception) {
exception.printStackTrace();
return formattedAmount;
}
return formattedAmount;
}
private val currencyFormatter = NumberFormat.getCurrencyInstance(LOCALE_AUS).configure()
private fun NumberFormat.configure() = apply {
maximumFractionDigits = 2
minimumFractionDigits = 2
}
fun Number.asCurrency(): String {
return currencyFormatter.format(this)
}
And then just use as
val x = 100000.234
x.asCurrency()
If you have the value stored in a String like me, which was coming from the server like "$20000.00".
You can do something like this in Kotlin (JetpackCompose):
#Composable
fun PrizeAmount(
modifier: Modifier = Modifier,
prize: String,
)
{
val currencyFormat = NumberFormat.getCurrencyInstance(Locale("en", "US"))
val text = currencyFormat.format(prize.substringAfter("$").toDouble())
...
}
Output: "$20,000.00"
NumberFormat.getCurrencyInstance(Locale("ES", "es")).format(number)
here is a kotlin version to Format Currency, here i'm getting an argument from another fragment from an input Field then it will be set in the textView in the main Fragment
fun formatArgumentCurrency(argument : String, textView: TextView) {
val valueText = requireArguments().get(argument).toString()
val dec = DecimalFormat("#,###.##")
val number = java.lang.Double.valueOf(valueText)
val value = dec.format(number)
val currency = Currency.getInstance("USD")
val symbol = currency.symbol
textView.text = String.format("$symbol$value","%.2f" )
}
You can easily achieve this with this small simple library.
https://github.com/jpvs0101/Currencyfy
Just pass any number, then it will return formatted string, just like that.
currencyfy (500000.78); // $ 500,000.78 //default
currencyfy (500000.78, false); // $ 500,001 // hide fraction (will round off automatically!)
currencyfy (500000.78, false, false); // 500,001 // hide fraction & currency symbol
currencyfy (new Locale("en", "in"), 500000.78); // ₹ 5,00,000.78 // custom locale
It compatible with all versions of Android including older versions!
i have 2 android project.
first project--> custom edit text that i made with custom regular expresion like this
private static final String QUANTITY_REGEX = "^\\d{0,4}(\\,\\d{0,3})?$";
second project --> project that use custom edit text from first project.
after that, i exported the first class to be a library on second project as a custom edittext.
the problem is :
as you can see the regular expression on first project only allowing 4 number to be written first, and 2 digit after "," symbol on edit text. but i want to make the custom edit text to be like this
isFocused: 1234,56
!isFocused : 1.234,56
how to make it possible. thx
Change your regex to ^\d{0,4}(.\d{0,4})?(,\d{0,2})?$
private static final String QUANTITY_REGEX ="^\\d{0,4}(.\\d{0,4})?(,\\d{0,2})?$";
The matches will be
1234
1234.5678
1234.5678,12
here what i`ve figure out... hope can usefull for other programmer
the concept :
1 edit text
when user focused on edittext (txt1), user only can write 4 number , a comma (",") and 3 another number after comma (",") --> 1234,567
when (txt1) onFocused = false (user click to another editText) txt1 show the number like this 1.234,567
here the code
VALIDATION CLASS
public class Validation {
// Regular Expression
// you can change the expression based on your need
private static final String EMAIL_REGEX = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
private static final String PHONE_REGEX = "\\d{3}-\\d{7}";
private static final String CURRENCY_REGEX = "^\\d{0,8}(\\,\\d{0,2})?$";
public String maxLength = Quantity.maxLengthFront;
public String QUANTITY_REGEX = null;
// Error Messages
private static final String REQUIRED_MSG = "required";
private static final String EMAIL_MSG = "invalid email";
private static final String PHONE_MSG = "###-#######";
private static final String CURRENCY_MSG = "invalid currency";
private static final String QUANTITY_MSG = "invalid quantity";
// call this method when you need to check email validation
public static boolean isEmailAddress(EditText editText, boolean required) {
return isValid(editText, EMAIL_REGEX, EMAIL_MSG, required);
}
// call this method when you need to check phone number validation
public static boolean isPhoneNumber(EditText editText, boolean required) {
return isValid(editText, PHONE_REGEX, PHONE_MSG, required);
}
// call this method when you need to check currency validation
public static boolean isCurrency(EditText editText, boolean required) {
return isValid(editText, CURRENCY_REGEX, CURRENCY_MSG, required);
}
public boolean isQuantity(EditText editText, boolean required){
return isValid(editText, QUANTITY_REGEX, QUANTITY_MSG, required);
}
// return true if the input field is valid, based on the parameter passed
public static boolean isValid(EditText editText, String regex, String errMsg, boolean required) {
String text = editText.getText().toString().trim();
// clearing the error, if it was previously set by some other values
editText.setError(null);
// text required and editText is blank, so return false
if ( required && !hasText(editText) ) return false;
// pattern doesn't match so returning false
if (required && !Pattern.matches(regex, text)) {
editText.setError(errMsg);
return false;
};
// pattern doesn't match so returning false
if (!required && !Pattern.matches(regex, text)) {
editText.setError(errMsg);
return false;
};
return true;
}
// check the input field has any text or not
// return true if it contains text otherwise false
public static boolean hasText(EditText editText) {
String text = editText.getText().toString().trim();
editText.setError(null);
// length 0 means there is no text
if (text.length() == 0) {
editText.setError(REQUIRED_MSG);
return false;
}
return true;
}
}
QUANTITY CLASS
public class Quantity extends EditText {
public static String maxLength;
public static String maxLengthFront = "4";
public static String regexCustom;
public boolean statusFocused = false;
public boolean allowBlank;
public String decimalPlaces;
String oldText;
Validation validation = new Validation();
public Quantity(Context context) {
super(context);
}
public Quantity(Context context, AttributeSet attrs) {
super(context, attrs);
// --- Additional custom code --
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.Quantity);
final int N = a.getIndexCount();
for (int i = 0; i < N; ++i) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.Quantity_decimalPlaces_quantity:
decimalPlaces = a.getString(attr);
// ...do something with delimiter...
break;
case R.styleable.Quantity_maxLength_quantity:
maxLength = "5";
// ...do something with fancyText...
doSetMaxLength();
break;
case R.styleable.Quantity_allowBlank_quantity:
allowBlank = a.getBoolean(attr, false);
// ...do something with fancyText...
break;
}
}
a.recycle();
this.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
this.setKeyListener(DigitsKeyListener.getInstance("1234567890-.,"));
this.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
if (statusFocused == true) {
maxLengthFront = "4";
if (validation.isQuantity(Quantity.this, false)) {
// Toast.makeText(getContext(), "Validation True", Toast.LENGTH_SHORT).show();
} else {
// Toast.makeText(getContext(), "Validation False",Toast.LENGTH_SHORT).show();
Quantity.this.setText(oldText);
Quantity.this.setSelection(Quantity.this.getText().length(), Quantity.this.getText().length());
}
} else {
maxLengthFront = "5";
}
validation.QUANTITY_REGEX = "^\\d{0," + maxLengthFront + "}(\\,\\d{0,3})?$";
}
public void beforeTextChanged(CharSequence s, int start, int count,int after) {
oldText = s.toString();
// Toast.makeText(getContext(), "Before change : " + s, Toast.LENGTH_SHORT).show();
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
this.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean gainFocus) {
// onFocus
String s;
if (gainFocus) {
maxLengthFront = "4";
validation.QUANTITY_REGEX = "^\\d{0," + maxLengthFront + "}(\\,\\d{0,3})?$";
s = Quantity.this.getText().toString().replace(".", "");
Quantity.this.setText(s);
statusFocused = true;
}
// onBlur
else {
maxLengthFront = "5";
validation.QUANTITY_REGEX = "^\\d{0," + maxLengthFront + "}(\\.\\d{0,3})?$";
Double number = Double.parseDouble(Quantity.this.getText().toString());
DecimalFormatSymbols symbol = DecimalFormatSymbols.getInstance();
symbol.setGroupingSeparator('.');
symbol.setDecimalSeparator(',');
DecimalFormat formatter = new DecimalFormat("###,###.###", symbol);
Quantity.this.setText(formatter.format(number));
statusFocused = false;
}
}
});
}
public Quantity(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// --- Additional custom code --
}
// #Override
// protected void onTextChanged(CharSequence text, int start,
// int lengthBefore, int lengthAfter) {
// // TODO Auto-generated method stub
// super.onTextChanged(text, start, lengthBefore, lengthAfter);
// Toast.makeText(getContext(), this.getText() + " - " + text.toString(),
// Toast.LENGTH_SHORT).show();
// }
public void doSetMaxLength() {
InputFilter[] FilterArray = new InputFilter[1];
FilterArray[0] = new InputFilter.LengthFilter(Integer.parseInt(maxLength, 10));
this.setFilters(FilterArray);
}
}
I am creating an Android application using Eclipse in which the user enters their lottery numbers. The app then retrieves the lottery numbers from the latest live draw using Jsoup to parse the html lottery numbers from the National Lottery Website. The user then pushes a check button after which a new activity opens displaying the match between the users numbers and the lottery draw numbers to check if the user has won the lottery. At this point I would like to have a button that allows the user to open the lottery webpage to enable them to check their prize, if they have matched their numbers. However I am having difficulty opening the browser. After the user has entered their numbers and hits check button, the program crashes, so they are not even reaching the point of comparing their numbers with the lottery numbers. I am getting the error that I am unable to start the activity DisplayNumbersActivity as there is a null pointer exception. Can anyone please help me to identify what the problem is with my code or how I could resolve it? Thanks in advance! I have included the main activity and DisplayNumbers activity code below.
public class DisplayNumbersActivity extends Activity {
private EditText urlText;
private Button checkWeb;
#SuppressLint("NewApi")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_numbers);
// Show the Up button in the action bar.
setupActionBar();
//get the message from the intent
Intent intent = getIntent();
String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
//create the text view
TextView textView = new TextView(this);
textView.setTextSize(20);
textView.setTextColor(Color.RED);
textView.setText(message);
//set the text view as the activity layout
setContentView(textView);
urlText = (EditText) findViewById(R.id.url_field);
checkWeb = (Button) findViewById(R.id.checkWeb);
//set up event handlers
checkWeb.setOnClickListener (new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
openBrowser();
}//onClick
});//setOnClickListener
urlText.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if (keyCode == KeyEvent.KEYCODE_ENTER) {
openBrowser();
return true;
}
return false;
}//onKey
});//setOnKeyListener
}//onCreate
//open a browser on the URL specified in the text box
private void openBrowser() {
Uri uri = Uri.parse(urlText.getText().toString());
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}//openBrowser
/**
* Set up the {#link android.app.ActionBar}, if the API is available.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void setupActionBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}//setUpActionBar
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}//onOptionsItemSelected
}//class
public class MainActivity extends Activity {
private final static String NATIONAL_LOTTERY_DRAW_URL = "http://www.national-lottery.co.uk/player/p/drawHistory.do";
public final static String EXTRA_MESSAGE = ".com.example.lottochecker.MESSAGE";
boolean bonus = false;
boolean jackpot = false;
int lottCount = 0;
Button check;
Integer [] numbers;
int bonusBall;
String userInput = "";
final int MAX = 49;
boolean validType = false;
int userGuess;
private LotteryDraw lotteryDraw;
#Override
//when the activity is created, call the layout class
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}//onCreate
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}//onCreateOptionsMenu
//called when the user clicks the send button
public void checkNumbers(View view) {
//set up an array of text boxes for the user to put in their numbers
EditText[] text_fields = new EditText[6];
//set up an array of string variables for holding user input
String[] str_nums = new String[6];
//set up an array to hold integer values having been converted from the user input as a String
int[] int_nums = new int[6];
//populate the array of text boxes with user input
text_fields[0] = (EditText) findViewById(R.id.enter_numbers);
text_fields[1] = (EditText) findViewById(R.id.enter_numbers2);
text_fields[2] = (EditText) findViewById(R.id.enter_numbers3);
text_fields[3] = (EditText) findViewById(R.id.enter_numbers4);
text_fields[4] = (EditText) findViewById(R.id.enter_numbers5);
text_fields[5] = (EditText) findViewById(R.id.enter_numbers6);
for(int i=0; i<6; i++)
{
str_nums[i] = text_fields[i].getText().toString();
// if the text box is empty, print error and stop processing.
// if not empty convert string to int and store in array
if(str_nums[i].equals(""))
{
Toast.makeText(MainActivity.this, "Please enter valid number in text box "+(i+1), Toast.LENGTH_LONG).show();
return;
}
else
{
int_nums[i] = Integer.parseInt(str_nums[i]);
}
}
// check validity of numbers entered
for(int i=0; i<6; i++)
{
// check numbers are in range
if (int_nums[i] < 1 || int_nums[i] > MAX)
{
Toast.makeText(MainActivity.this, "Number " + int_nums[i] + " in text box " + (i+1) + " is out of range. Please enter a number between 1 and 49", Toast.LENGTH_LONG).show();
return;
}
// check for duplicates
for(int j=0; j<6; j++)
{
if(i != j)
{
if (int_nums[i] == int_nums[j])
{
Toast.makeText(MainActivity.this, "The number " + int_nums[i] + " is dublicated in text boxes " + (i+1) + " and " + (j+1) + ". Duplicates can not be accepted", Toast.LENGTH_LONG).show();
return;
}
}
}
}
// numbers entered are valid
int matches = 0;
boolean bonus_match = false;
final int[] LOTTONUMBERS = lotteryDraw.getNumbers();
// check the 6 lotto numbers
for(int lotto_num = 0; lotto_num < 6; lotto_num++)
{
for(int user_num = 0; user_num < 6; user_num++)
{
if(LOTTONUMBERS[lotto_num] == int_nums[user_num])
{
matches++;
break;
}
}
}
// check the bonus ball
for(int user_num = 0; user_num < 6; user_num++)
{
if(lotteryDraw.getBonusBall() == int_nums[user_num])
{
bonus_match = true;
break;
}
}
//inform the user of the results
String output = "The lotto numbers are:\n";
for(int i=0; i<6; i++)
{
output = output + LOTTONUMBERS[i] + " ";
}
output = output + " bonus: " + lotteryDraw.getBonusBall();
output = output + "\n\nYour numbers are:\n";
for(int i=0; i<6; i++)
{
output = output + str_nums[i] + " ";
}
output = output + "\n\nYou have matched "+ matches + " numbers ";
if(bonus_match)
{
output = output + "and the bonus";
}
if(matches == 6)
{
output = output + "\n\nCONGRATULATIONS - YOU HAVE WON THE JACKPOT";
}
else if (matches >= 3)
{
output = output + "\n\nCONGRATULATIONS - you have won a prize";
}
else
{
output = output + "\n\nBad Luck - not enough matches to win";
}
//display the lottery results to the new activity
Intent intent = new Intent(this, DisplayNumbersActivity.class);
intent.putExtra(EXTRA_MESSAGE, output);
startActivity(intent);
}//method
public void getLotteryDrawFromWebsite(View view) {
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
new DownloadWebpageTask().execute(NATIONAL_LOTTERY_DRAW_URL);
} else {
//TODO: add error info
}
}
private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result) {
lotteryDraw = extractLotteryDraw(result);
TextView tv = (TextView) findViewById(R.id.textView1);
tv.setText(lotteryDraw.toString());
//when the lottery draw has been received enable the check button for the user to check numbers
Button checkNumbers = (Button)findViewById(R.id.check);
checkNumbers.setEnabled(true);
//Log.d("DownloadWebpageTask", lotteryDraw.toString());
}
}
private String downloadUrl(String myurl) throws IOException {
InputStream is = null;
// Only display the first 100000 characters of the retrieved
// web page content.
int len = 200000;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setRequestProperty( "User-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4" );
// Starts the query
conn.connect();
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
}
private String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return (new String(buffer)).trim();
}
private LotteryDraw extractLotteryDraw(String html) {
Log.d("extractLotteryDraw",html);
LotteryDraw lotteryDraw = new LotteryDraw();
Document doc = Jsoup.parse(html);
Elements elements = doc.getElementsByClass("drawhistory");
//System.out.println(elements.toString());
Element table = elements.first();
Element tbody = table.getElementsByTag("tbody").first();
Element firstLottoRow = tbody.getElementsByClass("lottorow").first();
Element dateElement = firstLottoRow.child(0);
System.out.println(dateElement.text());
Element gameElement = firstLottoRow.child(1);
System.out.println(gameElement.text());
Element noElement = firstLottoRow.child(2);
System.out.println(noElement.text());
String[] split = noElement.text().split(" - ");
int[] numbers = new int[split.length];
int i = 0;
for (String strNo : split) {
numbers[i] = Integer.valueOf(strNo);
i++;
}
lotteryDraw.setNumbers(numbers);
Log.v("DEBUG", "the value of numbers is " + numbers);
Element bonusElement = firstLottoRow.child(3);
Integer bonusBall = Integer.valueOf(bonusElement.text());
lotteryDraw.setBonusBall(bonusBall);
Log.v("DEBUG", "the value of numbers is " + numbers);
return lotteryDraw;
}//extractLotteryDraw
}//class
Add activity reference in your AndroidManifest.xml inside application tag
<activity android:name=".DisplayNumbersActivity"/>
I am showing text as link in my text view by android:autoLink="web" property. And it showing successfuly. But now i also want to show text as link which starts from #, for example "FleeGroups" in word "User pressed FOH button of this post via #FleeGroups"
Use a Spanable String
public class MainActivity extends Activity {
TextView tv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String s= "User pressed FOH button of this post via #FleeGroups";
tv = (TextView) findViewById(R.id.tv);
String split[] = s.split("#");
SpannableString ss1= new SpannableString(split[1]);
Log.i("....",""+split[0]+"........."+split[1]);
ss1.setSpan(new MyClickableSpan(split[1]), 0,split[1].length(), 0);
tv.append(split[0]);
tv.append(ss1);
tv.setMovementMethod(LinkMovementMethod.getInstance());
}
class MyClickableSpan extends ClickableSpan
{
String mystring;
public MyClickableSpan(String s)
{
mystring =s;
}
#Override
public void updateDrawState(TextPaint ds) {
// TODO Auto-generated method stub
super.updateDrawState(ds);
ds.setColor(Color.BLUE);
}
#Override
public void onClick(View widget) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, mystring, 1000).show();
}
}
}
More on styling #
http://www.chrisumbel.com/article/android_textview_rich_text_spannablestring
Snap shot
For reference if you need it later.
You can also use a regex to match words that start with #
String s= "User pressed #FOH button of this post via #FleeGroups some text";
Matcher matcher = Pattern.compile("#\\s*(\\w+)").matcher(s);
while (matcher.find()) {
spanstring= matcher.group(1);
Log.i(".............",spanstring);
}
You could use Html.fromHtml() and then set the LinkMovementMethod movement method.
Like this:
String link = "#FleeGroups";
String message = "User pressed FOH button of this post via ";
textView.setText(Html.fromHtml(message + link));
textView.setMovementMethod(LinkMovementMethod.getInstance());
/*Method in which you can pass the string to convert the into
spannableString and call this method form where ever you want
to set the text. It even work if you have mutiple # symbols
in your string.*/
TextView tv=(TextView) findViewById(R.id.textview);
tv.setText(getSpannableString("hi #StackOverFlow android"));
public SpannableStringBuilder getSpannableString(String str) {
SpannableStringBuilder builder = new SpannableStringBuilder();
String feed = str.replaceAll("\n", " ");
String[] individualfeed = feed.split(" ");
for (int i = 0; i < individualfeed.length; i++) {
if (individualfeed[i].contains("#")
) {
SpannableString redSpannable = new SpannableString(
individualfeed[i] + " ");
Pattern p = Pattern.compile(".*(\\w+)");
Matcher m = p.matcher(individualfeed[i]);
String str123 = null;
if (m.find()) {
str123 = m.group(1);
}
int startFrom = 0;
if (individualfeed[i].contains("#")) {
startFrom = individualfeed[i].indexOf("#");
}
if(individualfeed[i].trim().length()==1)
{
builder.append(individualfeed[i] + " ");
continue;
}
// I am using Green Color in this code change it accordingly
redSpannable.setSpan(
new ForegroundColorSpan(Color.parseColor("#00FF00")),
startFrom, individualfeed[i].lastIndexOf(str123) + 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
final String tag = (String) individualfeed[i].subSequence(
startFrom, individualfeed[i].lastIndexOf(str123) + 1);
builder.append(redSpannable);
} else {
builder.append(individualfeed[i] + " ");
}
}
return builder;
}
I am using JSoup parser to find particular parts of a html document (defined by regex) and highlight it by wrapping the found string in <span> tag. Here is my code that does the highlighting -
public String highlightRegex() {
Document doc = Jsoup.parse(htmlContent);
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
#Override
public void tail(Node node, int depth) {
if (node instanceof Element) {
Element elem = (Element) node;
StringBuffer obtainedText;
for(Element tn : elem.getElementsMatchingOwnText(pat)) {
Log.e("HELLO", tn.baseUri());
Log.e("HELLO", tn.text());
obtainedText = new StringBuffer(tn.ownText());
mat = pat.matcher(obtainedText.toString());
int nextStart = 0;
while(mat.find(nextStart)) {
obtainedText = obtainedText.replace(mat.start(), mat.end(), "<span>" + mat.group() + "</span>");
nextStart = mat.end() + 1;
}
tn.text(obtainedText.toString());
Log.e("HELLO" , "AFTER:" + tn.text());
}
}
}
#Override
public void head(Node node, int depth) {
}
});
nd.traverse(doc.body());
return doc.toString();
}
It does work but the tag <span> is visible inside the webview. What am I doing wrong?
Looks like no one knows. Here's some code that i've come up with. Slow and inefficient but works anyway. Suggestions are accepted :)
This class can be used to highlight any html using a regex.
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;
}
}
}
you have to call these two methods to get the highlighted html -
Highlighter hl = new Highlighter("abc def", htmlString);
String newhtmlString = hl.getHighlightedHtml();
This will highlight everything that matches the regex (abc)|(def)*.
You can change the way you want the regex to be built by modifying buildRegexFromQuery() function.