Please i need help to optimize the input filter on my customEditText
Requirement:
min float = 0.001
max float = 1000.000
decimal number = 3
I want to avoid 0 from the user set but he can enter something like 0.01 , 0.001 , 1.555 and 1000.000
This is what i've tested so far:
I used this function to set the min and max
class EditTextInputFilter(min: Float, max: Float) : InputFilter {
private val min: Float = min.coerceAtMost(max)
private val max: Float = min.coerceAtLeast(max)
override fun filter(source: CharSequence, i: Int, i2: Int, spanned: Spanned, i3: Int, i4: Int): CharSequence? {
try {
val input = (spanned.toString() + source.toString()).toFloatOrZero()
if (isInRange(min, max, input)) {
return null
}
} catch (nfe: NumberFormatException) {
Logger.error(nfe.localizedMessage!!)
}
return ""
}
private fun isInRange(min: Float, max: Float, value: Float): Boolean {
return value in min..max
}
}
I used this Decimal filter to remove comment and un-useful dots
class DecimalFilter(private val decimalDigits: Int) : InputFilter {
override fun filter(source: CharSequence, i: Int, i2: Int, spanned: Spanned, i3: Int, i4: Int): CharSequence? {
var dotPos = -1
val len = spanned.length
for (decimalsI in 0 until len) {
val c = spanned[decimalsI]
if (c == '.' || c == ',') {
dotPos = decimalsI
break
}
}
if (dotPos >= 0) {
// protects against many dots
if (source == "." || source == ",") return ""
// if the text is entered before the dot
if (i4 <= dotPos) return null
if (len - dotPos > decimalDigits) return ""
}
return null
}
}
class CustomEditText : TextInputEditText {
private var decimals = 3
private var min = 0.001f
private var max = 1000f
private lateinit var oldFilters: MutableList<InputFilter>
constructor(context: Context?) : super(context!!) {
init(null)
}
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs) {
init(attrs)
}
constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context!!, attrs, defStyle) {
init(attrs)
}
private fun init(attrs: AttributeSet?) {
isInEditMode
if (attrs != null) {
val a = context.theme.obtainStyledAttributes(attrs, R.styleable.CustomEditText, 0, 0)
decimals = a.getInt(R.styleable.CustomEditText_decimals, decimals)
min = a.getFloat(R.styleable.CustomEditText_min, min)
max = a.getFloat(R.styleable.CustomEditText_max, max)
}
oldFilters = filters.toMutableList()
addFilters()
}
private fun addFilters() {
val inputFilters = ArrayList<InputFilter>(oldFilters.size + 1)
inputFilters.addAll(oldFilters)
inputFilters.add(EditTextInputFilter(min, max))
inputFilters.add(DecimalFilter(decimals))
filters = inputFilters.toTypedArray()
}
fun setMin(min: Float) {
this.min = min
addFilters()
}
fun setMax(max: Float) {
this.max = max
addFilters()
}
fun setDecimals(decimals: Int) {
this.decimals = decimals
addFilters()
}
}
My problem now is, i can not enter 0
The problem is that you're validating the field as the user types and since the range of allowed values is 0.001..1000.000 you cannot type the number 0 (even if the intention is to enter 0.1).
This is not a coding but a specification problem.
The way it's specified is:
don't accept values outside 0.001..1000.000
validate as the user types
It's impossible to meet these requirements. I see a couple of solutions:
validate once the user submits (not as the user types)
validate without blocking the user input (by using setError in a TextWatcher)
allow 0 values in your filter (range 0..1000.000) but add another validation when the user submits to eliminate the 0
allow 0 values in your filter (range 0..1000.000) and disable the submit button (or whatever you have to process the input) if the value isn't in range
From a ux perspective I'd vote for the last option and maybe also indicate to the user what values are allowed so they know why they can't continue
#EmanuelMoecklin After reading your proposal answer i reviewed my code with this code below and it works perfectly and also i can re-use it for other input field.
I use my EditTextInputFilter class to set the min and max. The DecimalFilter class to set the number of decimal
class EditTextInputFilter(min: Float, max: Float) : InputFilter {
private val min: Float = min.coerceAtMost(max)
private val max: Float = min.coerceAtLeast(max)
override fun filter(source: CharSequence, i: Int, i2: Int, spanned: Spanned, i3: Int, i4: Int): CharSequence? {
try {
val input = (spanned.toString() + source.toString()).toFloatOrZero()
if (isInRange(min, max, input)) {
return null
}
} catch (nfe: NumberFormatException) {
Logger.error(nfe.localizedMessage!!)
}
return ""
}
private fun isInRange(min: Float, max: Float, value: Float): Boolean {
return value in min..max
}
}
class DecimalFilter(private val decimalDigits: Int) : InputFilter {
override fun filter(source: CharSequence, i: Int, i2: Int, spanned: Spanned, i3: Int, i4: Int): CharSequence? {
var dotPos = -1
val len = spanned.length
for (decimalsI in 0 until len) {
val c = spanned[decimalsI]
if (c == '.' || c == ',') {
dotPos = decimalsI
break
}
}
if (dotPos >= 0) {
// protects against many dots
if (source == "." || source == ",") return ""
// if the text is entered before the dot
if (i4 <= dotPos) return null
if (len - dotPos > decimalDigits) return ""
}
return null
}
}
I've created another validate function :
private fun validateInput(input: String): Boolean {
val double = input.toDoubleOrNull()
return double != null && double in 0.001f...1000f
}
Than i set the input filter like this on my input field:
binding.valueTiet.filters = arrayOf(EditTextInputFilter(0f, 1000f), DecimalFilter(3))
binding.valueTiet.doAfterTextChanged {
if (validateInput(it)) {
binding.valueTietTil.error = ""
Logger.info("Value accepted")
} else {
binding.valueTietTil.error = "Allowed range [0.001-1000]"
}
}
Related
I have such a piece of code
private fun snnFormatValidation() {
binding.snnText.addTextChangedListener(object : TextWatcher {
private var spaceDeleted = false
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
//in-progress
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
val charDeleted = s.subSequence(start, start + count)
spaceDeleted = " " == charDeleted.toString()
}
override fun afterTextChanged(editable: Editable) {
binding.snnText.removeTextChangedListener(this)
val cursorPosition: Int = binding.snnText.selectionStart
val withSpaces = formatText(editable)
binding.snnText.setText(withSpaces)
binding.snnText.setSelection(cursorPosition + (withSpaces.length - editable.length))
if (spaceDeleted) {
binding.snnText.setSelection(binding.snnText.selectionStart - 1)
spaceDeleted = false
}
binding.snnText.addTextChangedListener(this)
}
private fun formatText(text: CharSequence): String {
val formatted = StringBuilder()
var count = 0
for (i in text.indices) {
if (Character.isDigit(text[i])) {
if (count % 4 == 0 && count > 0) formatted.append("-")
formatted.append(text[i])
++count
}
}
return formatted.toString()
}
})
}
But when I enter 5 characters, I catch the following error
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.siolla.app, PID: 28136
java.lang.IndexOutOfBoundsException: setSpan (7 ... 7) ends beyond length 6
at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1326)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:685)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:677)
at androidx.emoji2.text.SpannableBuilder.setSpan(SpannableBuilder.java:140)
at android.text.Selection.setSelection(Selection.java:94)
at android.text.Selection.setSelection(Selection.java:78)
at android.text.Selection.setSelection(Selection.java:153)
at android.widget.EditText.setSelection(EditText.java:136)
at com.siolla.application.ui.snn.SnnFragment$snnFormatValidation$1.afterTextChanged(SnnFragment.kt:76)
Can anyone explain or show me what I'm doing wrong?
Error section -
how do I reference array types from the web3j library? I have a contract function which returns an array of addresses of whoever accessed the contract. I want to get these as arrays into a java/kotlin project. here's the code -
//gets an array of addresses from the contract
fun getAddressValues(): RemoteCall<Array<Address>> {
val function = Function(
functionGetAddressValues,
listOf(),
listOf<TypeReference<*>>(object : TypeReference<Array<Address>>() {})
)
// TODO: NOTE - THIS ARRAY IS FROM THE WEB3J LIBRARY "org.web3j.abi.datatypes.Array"
return executeRemoteCallSingleValueReturn(function, Array<Address>().javaClass) // TODO: error
}
I just want to get the class type for the array so that I can pass it on to this function -
executeRemoteCallSingleValueReturn(function, Array<Address>().javaClass) // TODO: error
And the error is on -
Array<Address>().javaClass
here's the contract code if you want a look -
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract VotingContract {
address[] addressStorage;
uint256 partyOneVotes;
uint256 partyTwoVotes;
uint256 partyThreeVotes;
constructor() {
partyOneVotes = 0;
partyTwoVotes = 0;
partyThreeVotes = 0;
}
function registerVote(uint256 num) public {
require(
num < 4 && num > 0,
"the given number is invalid as the number is out of range"
);
bool a = false;
address messageSender = msg.sender;
uint256 i;
for (i = 0; i < addressStorage.length; i++) {
if (messageSender == addressStorage[i]) {
a = false;
//set to true to block multiple entries.
}
}
require(a == false, "Your vote has already been registered");
addressStorage.push(messageSender);
if (num == 1) {
partyOneVotes++;
} else if (num == 2) {
partyTwoVotes++;
} else {
partyThreeVotes++;
}
}
function getAddressValues() public view returns (address[] memory) {
return addressStorage;
}
function getPartyOneVotes() public view returns (uint256) {
return partyOneVotes;
}
function getPartyTwoVotes() public view returns (uint256) {
return partyTwoVotes;
}
function getPartyThreeVotes() public view returns (uint256) {
return partyThreeVotes;
}
}
This is the contract function I am talking about -
function getAddressValues() public view returns (address[] memory) {
return addressStorage;
}
Any form of help is appriciated...
Maybe you are using the wrong type for you array. I use DynamicArray instead of Array.
This peace of code call a function with an address in parameter and return an DynamicArray. The values of the DynamicArray are displayed on the console :
val function = org.web3j.abi.datatypes.Function("getTokensFromOwner",
inputParameters,
Arrays.asList<TypeReference<*>>(object: TypeReference<org.web3j.abi.datatypes.DynamicArray<Uint256>>() {}))
val encodedFunction = FunctionEncoder.encode(function)
val transaction = Transaction.createEthCallTransaction("0xD8e5D841C0db4c4b95fa9fCEc48936F51Aeeaed8", "0x6Ac98A430D17ef564091bA470785f1389d0b9371", encodedFunction)
val response: EthCall = web3j.ethCall(transaction, DefaultBlockParameter.valueOf("latest")).sendAsync().get() // get the result of the smartcontract
val value = response.value
val someTypes = FunctionReturnDecoder.decode(value, function.outputParameters)
var tokens: DynamicArray<Uint256> = DynamicArray((someTypes.get(0).value as ArrayList<Uint256>).toList())
for (token: Uint256 in tokens.value)
{
Log.v("token ", token.value.toString())
}
smart contract function :
function getTokensFromOwner(address owner) external view returns (uint256[] memory)
{
uint256 nbTokens = balanceOf(owner);
uint256[] memory tokensId = new uint256[](nbTokens);
for (uint256 i=0; i< nbTokens;i++)
{
tokensId[i] = (tokenOfOwnerByIndex(owner, i));
}
return tokensId;
}
I hope that example will help you :)
I intend to recursively iterate all grids within a circle zone, the code below will perform depth-first-search. But after 204 stacks, java.lang.StackOverflowError will be thrown.
def geohash_circle_around_point(lat: Double, lon: Double, radius: Double) = {
def expand_neighbors_impl(ghCenter: GeoHash, ghCur: GeoHash, buffer: collection.mutable.Set[GeoHash]): Unit = {
// MARK: DP: check whether it's iterated already or not
if(buffer contains ghCur) {
return
}
buffer += ghCur
for(ghAround <- get4GeoHashAround(ghCur)) {
if(distanceBetweenGeohash(ghCenter, ghAround) <= radius) {
expand_neighbors_impl(ghCenter, ghAround, buffer)
}
}
}
def get4GeoHashAround(gh: GeoHash): Array[GeoHash] = {
Array(gh.getNorthernNeighbour, gh.getSouthernNeighbour, gh.getWesternNeighbour, gh.getEasternNeighbour)
}
def distanceBetweenGeohash(gh1: GeoHash, gh2: GeoHash) = {
haversine(gh1.getBoundingBoxCenterPoint.getLatitude, gh1.getBoundingBoxCenterPoint.getLongitude, gh2.getBoundingBoxCenterPoint.getLatitude, gh2.getBoundingBoxCenterPoint.getLongitude)
}
val ghCenter = GeoHash.withBitPrecision(lat, lon, 40)
val s = collection.mutable.Set[GeoHash]()
expand_neighbors_impl(ghCenter, ghCenter, s)
s.map(_.getBoundingBox)
}
The stacktrace is as follows:
Exception in thread "main" java.lang.StackOverflowError
at scala.collection.mutable.HashSet.index(HashSet.scala:40)
at scala.collection.mutable.FlatHashTable$class.findElemImpl(FlatHashTable.scala:126)
at scala.collection.mutable.FlatHashTable$class.containsElem(FlatHashTable.scala:121)
at scala.collection.mutable.HashSet.containsElem(HashSet.scala:40)
at scala.collection.mutable.HashSet.contains(HashSet.scala:57)
at Test$.Test$$expand_neighbors_impl$1(Test.scala:32)
at Test$$anonfun$Test$$expand_neighbors_impl$1$1.apply(Test.scala:39)
at Test$$anonfun$Test$$expand_neighbors_impl$1$1.apply(Test.scala:37)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
at Test$.Test$$expand_neighbors_impl$1(Test.scala:37)
at Test$$anonfun$Test$$expand_neighbors_impl$1$1.apply(Test.scala:39)
at Test$$anonfun$Test$$expand_neighbors_impl$1$1.apply(Test.scala:37)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
at Test$.Test$$expand_neighbors_impl$1(Test.scala:37)
....
Could anyone give some suggestions? Thanks!
P.S.
Implementation for equals and hashCode for GeoHash:
public boolean equals(Object obj) {
if(obj == this) {
return true;
} else {
if(obj instanceof GeoHash) {
GeoHash other = (GeoHash)obj;
if(other.significantBits == this.significantBits && other.bits == this.bits) {
return true;
}
}
return false;
}
}
public int hashCode() {
byte f = 17;
int f1 = 31 * f + (int)(this.bits ^ this.bits >>> 32);
f1 = 31 * f1 + this.significantBits;
return f1;
}
Seems like you really need more than 200 calls at 40 precision...
You might want to consider rewriting your recursion to be tail-recursive, in order to be optimized by the compiler. Here's a way to do this:
#tailrec
def expand_neighbors_impl(ghCenter: GeoHash, toGoThrough: List[GeoHash], buffer: Set[GeoHash] = Set()): Set[GeoHash] = {
toGoThrough.headOption match {
case None => buffer
case Some(ghCur) =>
if (buffer contains ghCur) {
expand_neighbors_impl(ghCenter, toGoThrough.tail, buffer)
}
else {
val neighbors = get4GeoHashAround(ghCur).filter(distanceBetweenGeohash(ghCenter, _) <= radius)
expand_neighbors_impl(ghCenter, neighbors ++: toGoThrough, buffer + ghCur)
}
}
}
def expand_neighbors_impl(ghCenter: GeoHash, ghCur: GeoHash): Set[GeoHash] =
expand_neighbors_impl(ghCenter, List(ghCur))
Besides using tail-recursion, it avoids using a mutable Set, which might give some unexpected complication.
I have found a solution to my problem here Create new column with function in Spark Dataframe
But i am having difficulty in converting the below code to Java since it's in Scala
import org.apache.spark.sql.functions._
val myDF = sqlContext.parquetFile("hdfs:/to/my/file.parquet")
val coder: (Int => String) = (arg: Int) => {if (arg < 100) "little" else "big"}
val sqlfunc = udf(coder)
myDF.withColumn("Code", sqlfunc(col("Amt")))
Can someone provide me the Java equivalent code for this?. I am stuck in converting below 2 lines
val coder: (Int => String) = (arg: Int) => {if (arg < 100) "little" else "big"}
val sqlfunc = udf(coder)
Thanks,
Create your User Defined Function:
public class CodeUdf implements UDF1<Integer, String>{
#Override
public String call(Integer integer) throws Exception {
if(integer < 100)
return "little";
else
return"big";
}
}
Tell Spark about it
sqlContext.udf().register("Code", new CodeUdf(), DataTypes.IntegerType);
Use it in a select.
df.selectExpr("value", "Code(value)").show();
import org.apache.spark.sql.functions._
val myDF = sqlContext.parquetFile("hdfs:/to/my/file.parquet")
//val coder: (Int => String) = (arg: Int) => {if (arg < 100) "little" else "big"}
//val sqlfunc = udf(coder)
myDF.selectExpr("Code", "case when Amt < 100 'little' else 'big' end ")
Do you know of any method to make sure users can only enter figures with a maximum number of decimals.
I'm not sure how to address this problem. In the MS SQL database I'm going to send data from my app I've got columns with this type decimal(8,3)
Now considering the data type of the column that's finally going to store the value I want to validate in Android, I've considered these two cases:
If the user enters a number with no decimals, the maximum number of digits must be 8
If the user enters a number with decimals, the maximum number of digits must be 8 (including the digits to the right of the decimal point)
Now I'm sure about the first case, but not so much about the second. Is it right to keep the number of maximum digits fixed(for example, always 8)? Or should I consider allowing a maximum of 8 digits to the left and 3 to the right of the decimal point?
Either way this is what I've been trying in Android:
mQuantityEditText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
String str = mQuantityEditText.getText().toString();
DecimalFormat format = (DecimalFormat) DecimalFormat
.getInstance();
DecimalFormatSymbols symbols = format.getDecimalFormatSymbols();
char sep = symbols.getDecimalSeparator();
int indexOFdec = str.indexOf(sep);
if (indexOFdec >= 0) {
if (str.substring(indexOFdec, str.length() - 1).length() > 3) {
s.replace(0, s.length(),
str.substring(0, str.length() - 1));
}
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
});
Even though, the above code handles the maximum number of decimal places. It does not limit the total number of digits allowed in the EditText.
Do you think you could help me improve my code so that it handles both the maximum number of decimal places and the total number of digits allowed in a EditText (considering both numbers to the left and to the right of the decimal point)
EDIT
Well, now I'm trying what João Sousa suggested and here's what I've tried:
1) I defined a class that implements InputFilter
public class NumberInputFilter implements InputFilter {
private Pattern mPattern;
public NumberInputFilter(int precision, int scale) {
String pattern="^\\-?(\\d{0," + (precision-scale) + "}|\\d{0," + (precision-scale) + "}\\.\\d{0," + scale + "})$";
this.mPattern=Pattern.compile(pattern);
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned destination, int destinationStart, int destinationEnd) {
if (end > start) {
// adding: filter
// build the resulting text
String destinationString = destination.toString();
String resultingTxt = destinationString.substring(0, destinationStart) + source.subSequence(start, end) + destinationString.substring(destinationEnd);
// return null to accept the input or empty to reject it
return resultingTxt.matches(this.mPattern.toString()) ? null : "";
}
// removing: always accept
return null;
}
}
2) Tried to use the class like this :
mQuantityEditText.setFilters(new InputFilter[] { new NumberInputFilter(8,3)} );
I would go for a filter in the edit text itself with the power of regex. First the regex expression:
^\-?(\d{0,5}|\d{0,5}\.\d{0,3})$
Maybe there are multiple ways to improve this expression, but this does trick.
And now just set an input filter in the edittext, like this:
final String regex = "^\-?(\d{0,5}|\d{0,5}\.\d{0,3})$";
((EditText)rootView.findViewById(R.id.editText1)).setFilters(new InputFilter[] {
new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned destination, int destinationStart, int destinationEnd) {
if (end > start) {
// adding: filter
// build the resulting text
String destinationString = destination.toString();
String resultingTxt = destinationString.substring(0, destinationStart) + source.subSequence(start, end) + destinationString.substring(destinationEnd);
// return null to accept the input or empty to reject it
return resultingTxt.matches(regex) ? null : "";
}
// removing: always accept
return null;
}
}
});
Btw, I just tested this code and what it does is:
The user can enter a maximum of 8 digits;
As soon as the user enters a '.', the maximum decimal digits allowed are 8.
Did I correctly understand the problem you described?
-- EDIT
Ok, I was almost there. From what I understand, decimal(8,3) means at most 8 digits including digits to the left or right of the decimal point, ranging from -99999.999 to 99999.999.
At least that's what I understand from this sentence Standard SQL requires that DECIMAL(5,2) be able to store any value with five digits and two decimals, so values that can be stored in the salary column range from -999.99 to 999.99. Even though it's from the MySQL documentation the MSSQL docs seem to do the same.
I have answser for you, me also suffered lot in this kind of situation.:D :P
I have implemented this for maximum of 4 digits to the left and 2 to the right of the decimal point ex: 4444.99
so small changes need to implement what i did:
Need to do following changes
1) copy CustomTextWatcher.java to track input of editText.
import java.text.NumberFormat;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;
public class CustomTextWatcher implements TextWatcher {
private NumberFormat nf = NumberFormat.getNumberInstance();
private EditText et;
private String tmp = "";
private int moveCaretTo;
private static final int INTEGER_CONSTRAINT = 4;
private static final int FRACTION_CONSTRAINT = 2;
private static final int MAX_LENGTH = INTEGER_CONSTRAINT
+ FRACTION_CONSTRAINT + 1;
public CustomTextWatcher(EditText et) {
this.et = et;
nf.setMaximumIntegerDigits(INTEGER_CONSTRAINT);
nf.setMaximumFractionDigits(FRACTION_CONSTRAINT);
nf.setGroupingUsed(false);
}
public int countOccurrences(String str, char c) {
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == c) {
count++;
}
}
return count;
}
#Override
public void afterTextChanged(Editable s) {
et.removeTextChangedListener(this); // remove to prevent stackoverflow
String ss = s.toString();
int len = ss.length();
int dots = countOccurrences(ss, '.');
boolean shouldParse = dots <= 1
&& (dots == 0 ? len != (INTEGER_CONSTRAINT + 1)
: len < (MAX_LENGTH + 1));
if (shouldParse) {
if (len > 1 && ss.lastIndexOf(".") != len - 1) {
try {
if (ss.contains(".")) {
String[] integerFractionStrings = ss.split("\\.");
Log.v("Check SS ", ss);
Log.v("second string", "Found"
+ integerFractionStrings.length);
if (integerFractionStrings.length > 1) {
Log.v("integerFractionStrings",
integerFractionStrings[1]);
if (integerFractionStrings[1].length() == 1
&& integerFractionStrings[1].charAt(0) == '0') {
et.setText(ss);
Log.v("second string", "size 1");
} else {
Log.v("second string", "> 1");
Double d = Double.parseDouble(ss);
if (d != null) {
et.setText(nf.format(d));
}
}
}
} else {
Log.v("First string", "No dot");
Double d = Double.parseDouble(ss);
if (d != null) {
et.setText(nf.format(d));
}
}
} catch (NumberFormatException e) {
}
}
} else {
Log.v("second string", "size 1");
et.setText(tmp);
}
et.addTextChangedListener(this); // reset listener
// tried to fix caret positioning after key type:
if (et.getText().toString().length() > 0) {
if (dots == 0 && len >= INTEGER_CONSTRAINT
&& moveCaretTo > INTEGER_CONSTRAINT) {
moveCaretTo = INTEGER_CONSTRAINT;
} else if (dots > 0 && len >= (MAX_LENGTH)
&& moveCaretTo > (MAX_LENGTH)) {
moveCaretTo = MAX_LENGTH;
}
try {
et.setSelection(et.getText().toString().length());
// et.setSelection(moveCaretTo); <- almost had it :))
} catch (Exception e) {
}
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
moveCaretTo = et.getSelectionEnd();
tmp = s.toString();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
int length = et.getText().toString().length();
if (length > 0) {
moveCaretTo = start + count - before;
}
}
}
2) set this class to check your editText by following.
EditText review_food_Price;
review_food_Price = (EditText) findViewById(R.id.food_Price);
review_food_Price.setRawInputType(InputType.TYPE_CLASS_NUMBER
| InputType.TYPE_NUMBER_FLAG_DECIMAL);
review_food_Price.addTextChangedListener(new CustomTextWatcher(
review_food_Price));
Hope you can convert my code according to your need.
The problem that you describe is precisely what a Masked EditText is meant to be used for. :)