Limit Decimal Places in EditText - java

I'm using an EditText Field where the user can specify an amount of money.
I set the inputType to numberDecimal which works fine, except that this allows to enter numbers such as 123.122 which is not perfect for money.
I wrote some custom InputFilter method and it's working like this .User can 5 elements before dot and after dot-two,but not working correctly
My goals are :
1) use should input maximum 9999.99
2) If user starting from 0 and second element is also 0,It must replace with .(for example 0.0) and after two elements after dot(like this 0.01)
here is a my code
public class DecimalDigitsInputFilter implements InputFilter {
Pattern mPattern;
public DecimalDigitsInputFilter(int digitsBeforeZero,int digitsAfterZero) {
mPattern=Pattern.compile("[0-9]*" + (digitsBeforeZero-1) + "}+((\\.[0-9]*" + (digitsAfterZero-1) + "})?)||(\\.)?");
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
Matcher matcher=mPattern.matcher(dest);
if(!matcher.matches())
return "";
return null;
}
}
I'm calling this method like this
amountValue.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
How I can rewrite my code to solved my issues ?
thanks

Your constructed regex is all wrong.
The first part of your regex is this expression:
"[0-9]*" + (digitsBeforeZero-1) + "}+"
Say digitsBeforeZero = 5, that gets you:
[0-9]*4}+
That's not the correct regex. The regex is supposed to be:
[0-9]{1,5}
meaning between 1 and 5 digits.

Related

How to limit the EditText input only allow 3 digits of number either integer or decimal

As the title says, I set up an EditText in my activity and want to limit the input to only numbers. However, it doesn't matter if it is a decimal number or integer. I do require the number of digits is limited at 3. For example, the input of '123', '1.23', '12.3' are all legit input.
'1234', '123.', '.123' are all illegal input.
I have tried to set up
android:inputType = "numberDecimal"
in the xml file.
And set the max length to 4.
edit:
I also tried following code:
InputFilter filter = new InputFilter() {
//^\-?(\d{0,5}|\d{0,5}\.\d{0,3})$
//^\-?(\d{0,3}|\d{0,2}\.\d{0,1}|\d{0,1}\.\d{0,2})$
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (end > start) {
// adding: filter
// build the resulting text
String destinationString = dest.toString();
String resultingTxt = destinationString.substring(0, dstart) + source.subSequence(start, end) + destinationString.substring(dend);
// return null to accept the input or empty to reject it
return resultingTxt.matches("^\\-?(\\d{0,3}|\\d{0,2}\\.\\d{0,1}|\\d{0,1}\\.\\d{0,2})$") ? null : "";
}
return null;
}
};
I did modified the regex from the sample code mentioned by #Suman Dash.
My understanding of the regex
^\-?(\d{0,3}|\d{0,2}\.\d{0,1}|\d{0,1}\.\d{0,2})$
is to allow certain pattern of number input such as #.##, ##.# and ###.
When I test the code, the pattern #.## and ##.# are working fine, but the pattern ### also allow input like ".##", for example, ".88" as legit input. And it treats the decimal point as a legit number, so I can only input ".88", not ".123". Anyway, I don't want any number starts with the decimal point.
How can I eliminate that?
What's the best way to achieve this goal? Thanks!
InputFilter filter = new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
for (int i = start; i < end; ++i)
{
if (!Pattern.compile("[1234567890\.]*").matcher(String.valueOf(source.charAt(i))).matches())
{
return "";
}
}
return null;
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText ntxt =(EditText)findViewById(R.id.numberEditTextbox) ;
ntxt.setFilters(new InputFilter[]{filter,new InputFilter.LengthFilter(4)});
}
This code may help you.

Decimal points in calculator android

I am creating a calculator app in android. The issue is how can I check for existence of two decimals in a single numeric value . Currently my calculator allows inputs such as 1.2.3 which is illegal . It must allow a decimal if an operator has been used. Eg 1.1+2.2 is legal but 1.1.1+2.2 isn't.
Here is my code for decimal handling:
public class TrialCalculator extends AppCompatActivity implements Button.OnClickListener {
Button btn1, btn2, btnBack, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0, btnPlus, btnMinus, btnMul, btnDiv, btnEquals, btnClear, btnDecimal, btnPercent;
EditText calcResult;
double number = 0;
private char opcode = '1';
private void handleDecimal() {
if (opcode == 0) clear();
if (calcResult.getText() == null) {
calcResult.setText("0.");
calcResult.setSelection(2);
} else {
String txt = calcResult.getText().toString();
if (txt.lastIndexOf(".")<txt.length()-1) {
calcResult.append(".");
}
}
}
}
I am calling the buttonDot from onClick Method.
One solution is to have a flag which keeps track of the decimal:
class MyCalculator {
private hasDecimal = false;
// ...
}
Set this flag to true the first time that the user types a decimal. Then check the flag to see if a decimal has been previously typed.
Of course, this only works if you are responding to each key press directly rather than getting the entire input from a EditText after the user has typed the entire number.
You can use regex with matches function
\\d* mean match zero or more digits
(\\.\\d+)? match a . followed by one or more digits , ? mean matches between zero or one times of given group
Regex Demo : note with matches function in java, we don't need ^ start and $ ending anchors
Code
if (txt.matches("\\d*(\\.\\d+)?")) {
// number has one decimal
}
else{
// number has more than one decimal
}
Note: if you don't want to allow values like .5 then use \\d+ instead of \\d* as
\\d+(\\.\\d+)?
As suggested by #Code-Apprentice , if you want to accept values like 4343. etc
you can use
\\d*(\\.\\d*)?
Using text watcher
calcResult.addTextChangedListener(new TextWatcher() {
#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 s) {
boolean flag = s.toString().matches("\\d*(\\.\\d*)?");
if(!flag){
// calcResult.setError...
// you display a toast
}
}
});
Update : To match multiple values with operators , you can use
(\\d*(\\.\\d*)?([+\\-*%\\]|$))*
RegEx demo
Test Cases
String pat="(\\d*(\\.\\d*)?([+\\-*%\\]|$))*";
System.out.println("4.5-3.3+3.4".matches(pat));
System.out.println(".5".matches(pat));
System.out.println("4".matches(pat));
System.out.println("4.5.5-4.4".matches(pat));
System.out.println("4.44.5+4.4.4".matches(pat));
System.out.println("4.".matches(pat));
Output :
true
true
true
false
false
true
lastIndexOf returns -1 if the character is not found in the string. So your condition txt.lastIndexOf(".")<txt.length()-1 is always true. You could change it to
txt.lastIndexOf(".") == -1
To check for existence of two decimals in a single no then split the string by decimal.
for e.g.,
String txt = calcResult.getText().toString();
String[] decimals = txt.split("\\.");
if(decimals.length > 2) {
// txt contains more than 1 decimal.
// Your logic ..
}

Word in a java string

I am very new to Java and as a starter I have been offered to try this at home.
Write a program that will find out number of occurences of a smaller string in a bigger string as a part of it as well as an individual word.
For example,
Bigger string = "I AM IN AMSTERDAM", smaller string = "AM".
Output: As part of string: 3, as a part of word: 1.
While I did nail the second part (as a part of word), and even had my go at the first one (searching for the word as a part of the string), I just don't seem to figure out how to crack the first part. It keeps on displaying 1 for me with the example input, where it should be 3.
I have definitely made an error- I'll be really grateful if you could point out the error and rectify it. As a request, I am curious learner- so if possible (at your will)- please provide an explanation as to why so.
import java.util.Scanner;
public class Program {
static Scanner sc = new Scanner(System.in);
static String search,searchstring;
static int n;
void input(){
System.out.println("What do you want to do?"); System.out.println("1.
Search as part of string?");
System.out.println("2. Search as part of word?");
int n = sc.nextInt();
System.out.println("Enter the main string"); searchstring =
sc.nextLine();
sc.nextLine(); //Clear buffer
System.out.println("Enter the search string"); search = sc.nextLine();
}
static int asPartOfWord(String main,String search){
int count = 0;
char c; String w = "";
for (int i = 0; i<main.length();i++){
c = main.charAt(i);
if (!(c==' ')){
w += c;
}
else {
if (w.equals(search)){
count++;
}
w = ""; // Flush old value of w
}
}
return count;
}
static int asPartOfString(String main,String search){
int count = 0;
char c; String w = ""; //Stores the word
for (int i = 0; i<main.length();i++){
c = main.charAt(i);
if (!(c==' ')){
w += c;
}
else {
if (w.length()==search.length()){
if (w.equals(search)){
count++;
}
}
w = ""; // Replace with new value, no string
}
}
return count;
}
public static void main(String[] args){
Program a = new Program();
a.input();
switch(n){
case 1: System.out.println("Total occurences: " +
asPartOfString(searchstring,search));
case 2: System.out.println("Total occurences: " +
asPartOfWord(searchstring,search));
default: System.out.println("ERROR: No valid number entered");
}
}
}
EDIT: I will be using the loop structure.
A simpler way would be to use regular expressions (that probably defeats the idea of writing it yourself, although learning regexes is a good idea because they are very powerful: as you can see the core of my code is 4 lines long in the countMatches method).
public static void main(String... args) {
String bigger = "I AM IN AMSTERDAM";
String smaller = "AM";
System.out.println("Output: As part of string: " + countMatches(bigger, smaller) +
", as a part of word: " + countMatches(bigger, "\\b" + smaller + "\\b"));
}
private static int countMatches(String in, String regex) {
Matcher m = Pattern.compile(regex).matcher(in);
int count = 0;
while (m.find()) count++;
return count;
}
How does it work?
we create a Matcher that will find a specific pattern in your string, and then iterate to find the next match until there is none left and increment a counter
the patterns themselves: "AM" will find any occurrence of AM in the string, in any position. "\\bAM\\b" will only match whole words (\\b is a word delimiter).
That may not be what you were looking for but I thought it'd be interesting to see another approach. An technically, I am using a loop :-)
Although writing your own code with lots of loops to work things out may execute faster (debatable), it's better to use the JDK if you can, because there's less code to write, less debugging and you can focus on the high-level stuff instead of the low level implementation of character iteration and comparison.
It so happens, the tools you need to solve this already exist, and although using them requires knowledge you don't have, they are elegant to the point of being a single line of code for each method.
Here's how I would solve it:
static int asPartOfString(String main,String search){
return main.split(search, -1).length - 1;
}
static int asPartOfWord(String main,String search){
return main.split("\\b" + search + "\\b", -1).length - 1
}
See live demo of this code running with your sample input, which (probably deliberately) contains an edge case (see below).
Performance? Probably a few microseconds - fast enough. But the real benefit is there is so little code that it's completely clear what's going on, and almost nothing to get wrong or that needs debugging.
The stuff you need to know to use this solution:
regex term for "word boundary" is \b
split() takes a regex as its search term
the 2nd parameter of split() controls behaviour at the end of the string: a negative number means "retain blanks at end of split", which handle the edge case of the main string ending with the smaller string. Without the -1, a call to split would throw away the trailing blank in this edge case.
You could use Regular Expressions, try ".*<target string>.*" (Replace target string with what you are searching for.
Have a look at the Java Doc for "Patterns & Regular Expressions"
To search for the occurrences in a string this could be helpful.
Matcher matcher = Pattern.compile(".*AM.*").matcher("I AM IN AMSTERDAM")
int count = 0;
while (matcher.find()) {
count++;
}
Here's an alternative (and much shorter) way to get it to work using Pattern and Matcher,or more commonly known as regex.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CountOccurances {
public static void main(String[] args) {
String main = "I AM IN AMSTERDAM";
String search = "AM";
System.out.printf("As part of string: %d%n",
asPartOfString(main, search));
System.out.printf("As part of word: %d%n",
asPartOfWord(main, search));
}
private static int asPartOfString(String main, String search) {
Matcher m = Pattern.compile(search).matcher(main);
int count = 0;
while (m.find()) {
count++;
}
return count;
}
private static int asPartOfWord(String main, String search) {
// \b - A word boundary
return asPartOfString(main, "\\b" + search + "\\b");
}
}
Output:
As part of string: 3
As part of word: 1
For the first part of your Exercise this should work:
static int asPartOfWord(String main, String search) {
int count = 0;
while(main.length() >= search.length()) { // while String main is at least as long as String search
if (main.substring(0,search.length()).equals(search)) { // if String main from index 0 until exclusively search.length() equals the String search, count is incremented;
count++;
}
main = main.substring(1); // String main is shortened by cutting off the first character
}
return count;
You may think about the way you name variables:
static String search,searchstring;
static int n;
While search and searchstring will tell us what is meant, you should write the first word in lower case, every word that follows should be written with the first letter in upper case. This improves readability.
static int n won't give you much of a clue what it is used for if you read your code again after a few days, you might use something more meaningful here.
static String search, searchString;
static int command;

HTML.fromHtml adds space at end of text?

In my app I use the Html.fromHtml(string).toString method to remove some <p> tags that are received when I parse some JSON.
If I leave the <p> tags on, the text fits the background perfectly (the background is a relative layout with wrap_content in both height and width.) However, if I use fromHtml to remove the <p> tags, suddenely there is a huge space below the text, which I believe is the fromHtml method adding in space at the end?
Any ideas?
EDIT:
Here are screenshots:
http://imgur.com/a/zIZNo
The one with <p> tags is the one that doesnt use fromHtml, obviously! :)
EDIT 2: Solution has been found, see my answer below. Thank you to Andro Selva for helping me by telling me about the hidden /n that was being added!
Solution was found:
fromHtml returns the type Spanned. So I assigned what was being returned to a variable, converted it to a string and then used the .trim() method on it.
It removed all white space at the end.
Yes what you thought about is really correct. It adds space to the bottom. But before that let me explain how this works.
You have to look at HTML class to see how it works.
To be simple, this is how it works: whenever your Html class looks at a <p> tag, what it does is simply append two "\n" chars to the end.
In this case the empty space you see at the bottom is actually because of the two \n appended to the end of the paragaraph.
And I have added the actual method of the Html class which is responsible for this action,
private static void handleP(SpannableStringBuilder text) {
int len = text.length();
if (len >= 1 && text.charAt(len - 1) == '\n') {
if (len >= 2 && text.charAt(len - 2) == '\n') {
return;
}
text.append("\n");
return;
}
if (len != 0) {
text.append("\n\n");
}
}
If you want to override this action, you have to override the Html class itself which is a bit tricky and can't be completed here.
EDIT
here is the link to the Html class,
Html class
If you are trying to use it in an object or trying to fit it in a specific place, try using <a> tag instead of a <p>, <p> adds returns carriages at the end, a writes none, but you have to remember to write the \n yourself with <b>, and you get to keep the style
The explanation by #Andro Selva is correct and there is not much to be done about it. Frustratingly, things get better for API 24 and later with the inclusion of flags in the call
Spanned fromHtml (String source,
int flags,
Html.ImageGetter imageGetter,
Html.TagHandler tagHandler);
and I suspect the FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH flag will reduce the double "\n\n" of the standard paragraph termination to that of the single "\n" of a line break
Given the history of Android versions out there ~I can't afford to write software for Android API 24+ exclusively! So... I found a kludge solution with the inclusion of 2 extra custom tags.
1. <scale factor="x.xx">... </scale>
2. <default>... </default>
both invoke the RelativeSizeSpan class through this method
private void ProcessRelativeSizeTag(float scalefactor, boolean opening, Editable output) {
int len = output.length();
if (opening) {
System.out.println("scalefactor open: " + scalefactor);
output.setSpan(new RelativeSizeSpan(scalefactor), len, len,
Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, RelativeSizeSpan.class);
int where = output.getSpanStart(obj);
scalefactor = ((RelativeSizeSpan)obj).getSizeChange();
output.removeSpan(obj);
System.out.println("scalefactor close: " + scalefactor);
if (where != len) {
output.setSpan(new RelativeSizeSpan(scalefactor), where, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
which is called from the custom TagHandler supplied to the Html.fromHtml method, viz:
private static class CustomTagHandler implements Html.TagHandler {
private void ProcessRelativeSizeTag(float scalefactor, boolean opening, Editable output) {
int len = output.length();
if (opening) {
//mSizeStack.push(scalefactor);
System.out.println("scalefactor open: " + scalefactor);
output.setSpan(new RelativeSizeSpan(scalefactor), len, len,
Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, RelativeSizeSpan.class);
int where = output.getSpanStart(obj);
scalefactor = ((RelativeSizeSpan)obj).getSizeChange();
output.removeSpan(obj);
//scalefactor = (float)mSizeStack.pop();
System.out.println("scalefactor close: " + scalefactor);
if (where != len) {
output.setSpan(new RelativeSizeSpan(scalefactor), where, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
...
final HashMap<String, String> mAttributes = new HashMap<>();
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
String Attr;
processAttributes(xmlReader);
if ("default".equalsIgnoreCase(tag)) {
ProcessRelativeSizeTag(mDefaultTextSize, opening, output);
return;
}
if ("scale".equalsIgnoreCase(tag)) {
Attr = mAttributes.get("factor");
if (Attr != null && !Attr.isEmpty()) {
float factor = parseFloat(Attr);
if (factor > 0)
ProcessRelativeSizeTag(factor, opening, output);
}
return;
...
}
}
To use, I set the text size of the Textview object to 1. That is, 1 pixel! I then set the required true text size required in the variable mDefaultTextSize. I have all the Html functionality inside an htmlTextView which extends TextView as:
public class htmlTextView extends AppCompatTextView {
static Typeface mLogo;
static Typeface mGAMZ;
static Typeface mBrush;
static Typeface mStandard;
int GS_PAINTFLAGS = FILTER_BITMAP_FLAG | ANTI_ALIAS_FLAG | SUBPIXEL_TEXT_FLAG | HINTING_ON;
static float mDefaultTextSize;
static Typeface mDefaultTypeface;
etc
}
which includes the public method
public void setDefaultTextMetrics(String face, float defaultTextSize) {
mDefaultTypeface = mStandard;
if (face != null) {
if ("gamz".equalsIgnoreCase(face)) {
mDefaultTypeface = mGAMZ;
} else {
if ("brush".equalsIgnoreCase(face)) {
mDefaultTypeface = mBrush;
}
}
}
setTypeface(mDefaultTypeface);
setTextSize(1);
mDefaultTextSize = defaultTextSize;
}
A simple ((htmlTextView)tv).setDefaultTextMetrics(null, 30); call sets my htmlTextView to use my standard typeface as default with a text size of 30.
Then when I give it this example to use in fromHtml:
<string name="htmlqwert">
<![CDATA[
<p><default><scale factor="1.5"><box> qwertQWERT </box></scale></default></p>
<p><default><scale factor="1.5"><box> qwertQWERT </box></scale></default></p>
<p><default><scale factor="1.5"><box> qwertQWERT </box></scale></default></p>
<p><default><scale factor="1.5"><box> qwertQWERT </box></scale></default></p>
]]>
</string>
my custom tag <box> just lets me highlight the background of the text. See the attached picture, showing one result using the <default> tag with the TextView text size set to 1 and the <default> tag invoking a RelevantSizeSpan by a factor of 30, and one with:
<string name="htmlqwert">
<![CDATA[
<p><scale factor="1.5"><box> qwertQWERT </box></scale></p>
<p><scale factor="1.5"><box>qwertQWERT</box></scale></p>
<p><scale factor="1.5"><box>qwertQWERT</box></scale></p>
<p><scale factor="1.5"><box>qwertQWERT</box></scale></p>
]]>
</string>
using no <default> tag but setting the TextView text size to 30 instead. In the first case the extra new line is still there but it is only 1 pixel high!
NB There is no real point to the <scale factor="1.5">...</scale> tags. They are just left over artefacts from other tests.
Results: Both examples below have 2 newlines between paragraphs but, in the one on the left, one of those lines is only 1 pixel high. I will leave it to the reader to figure out how to reduce it to zero, but do not use a text size of 0
This solution works for me
Create a helper method to replace all paragraph starting and ending tags and replace all with empty characters.
#Nullable
public static String removeParagraphTags(#Nullable String input) {
if (input == null) {
return null;
}
return input.replaceAll("<p>", "").replaceAll("</p>", "");
}
And Usage
String input = "<p>This is some text in a paragraph.</p>";
HtmlCompat.fromHtml(StringUtils.removeParagraphTags(input),HtmlCompat.FROM_HTML_MODE_COMPACT)

Limit EditText to text (A-z) only

I want an editText that only allows text input from A to z, no numbers or other characters. I've found out I have to use InputFilter but I don't understand how this code works.
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (!Character.isLetterOrDigit(source.charAt(i))) {
return "";
}
}
return null;
}
};
edit.setFilters(new InputFilter[]{filter});
The code you posted adds a custom filter to the EditText field. It checks to see if the character entered is not a number or digit and then, if so, returns an empty string "". That code is here:
if (!Character.isLetterOrDigit(source.charAt(i))) {
return "";
}
For your needs, you want to change the code slightly to check if the character is NOT a letter. So, just change the call to the static Character object to use the isLetter() method. That will look like this:
if (!Character.isLetter(source.charAt(i))) {
return "";
}
Now, anything that is not a letter will return an empty string.
Haven't actually done it, but check Androids NumberKeyListener. You can find the source code for it here:
http://www.java2s.com/Open-Source/Android/android-core/platform-frameworks-base/android/text/method/NumberKeyListener.java.htm
it does exactly the opposite of what you need, but that should be a good enough starting point.

Categories

Resources