I want to count the characters in a string and if the counter reach a limit puts a line break. But I don't know how to solve.
I found that word counter, but how to add a break to specific places?e.g after each 4th word.
public static int countWords(String s){
int wordCount = 0;
boolean word = false;
int endOfLine = s.length() - 1;
for (int i = 0; i < s.length(); i++) {
// if the char is a letter, word = true.
if (Character.isLetter(s.charAt(i)) && i != endOfLine) {
word = true;
// if char isn't a letter and there have been letters before,
// counter goes up.
} else if (!Character.isLetter(s.charAt(i)) && word) {
wordCount++;
word = false;
// last word of String; if it doesn't end with a non letter, it
// wouldn't count without this.
} else if (Character.isLetter(s.charAt(i)) && i == endOfLine) {
wordCount++;
}
}
return wordCount;
}
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 having a problem with the above task in my android application. I am accepting user input from the EditText widget in the form of String. I accepting numbers from the user so I have to parse them to integers so they can be compared with another array of integers. I have the line:
String message = editText.getText().toString()
then to try and parse the String to an int I have the code line:
int userNumbers = Integer.parseInt(message).
However when I attempt to compare the array userArray with the array numbers I am getting the error that "Incompatible operand types String and Integer.
Can anyone see where my problem is or how I can solve it? Here's my code:
Thanks in advance.
public class MainActivity extends Activity {
public final static String EXTRA_MESSAGE = ".com.example.lotterychecker.MESSAGE";
static boolean bonus = false;
static boolean jackpot = false;
static int lottCount = 0;
Button check;
Integer [] numbers;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//link to the intended web site and get the lottery numbers while the app is opening
try {
Document doc = Jsoup.connect("http://www.national-lottery.co.uk/player/p/drawHistory.do").userAgent("Mozilla").get();
Elements elements = doc.getElementsByClass("drawhistory");
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(" - ");
// set up an array to store numbers from the latest draw on the lottery web page
Integer [] numbers = new Integer [split.length];
int i = 0;
for (String strNo : split) {
numbers [i] = Integer.valueOf(strNo);
i++;
}
for (Integer no : numbers) {
System.out.println(no);
}
Element bonusElement = firstLottoRow.child(3);
Integer bonusBall = Integer.valueOf(bonusElement.text());
System.out.println("Bonus ball: " + bonusBall);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#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;
}
//called when the user clicks the send button
public void checkNumbers(View view) {
final int SIZE =6;
String [] userArray = new String[SIZE];
//create an intent to display the numbers
Intent intent = new Intent(this, DisplayNumbersActivity.class);
EditText editText = (EditText) findViewById(R.id.enter_numbers);
String message = editText.getText().toString();
intent.putExtra(EXTRA_MESSAGE, message );
startActivity(intent);
//parse string message to an int for user numbers
try{
int userNumbers = Integer.parseInt(message); //is this right?
}//try
catch (NumberFormatException e)
{
System.out.println("Not a number" + e.getMessage());
}
Toast.makeText(MainActivity.this, "Here are your numbers", Toast.LENGTH_LONG).show();
for (int count =0; count < SIZE; count ++)
{
if (check.isPressed())
{
userArray[count] = editText.getText().toString();
}
}//for
//compare the two arrays of integers
for (int loop = 0; loop < userArray.length; loop++)
{
for (int loopOther = 0; loopOther < numbers.length; loopOther++)
{
if (userArray[loop] == numbers[loopOther]) //how do I parse this?
{
lottCount++;
}else if (userArray[loop] == bonus)
{
bonus = true;
}
}//for
}//for main
You have this
Integer [] numbers; // numbers is an integer array
You have string array
String [] userArray = new String[SIZE]; // userArray is a string array
You compare like below
if (userArray[loop] == numbers[loopOther])
So you get the error Incompatible operand types String and Integer.
try
if (Integer.parseInt(userArray[loop]) == numbers[loopOther])
Enclosing the above with try catch block
String message = editText.getText().toString();
try{
int userNumbers = Integer.parseInt(message);
//is this right? yes
}
catch (NumberFormatException e)
{
e.printStacktrace();
}
Change String to Int here:
for (int loop = 0; loop < userArray.length; loop++)
{
for (int loopOther = 0; loopOther < numbers.length; loopOther++)
{
if (Integer.valueOf(userArray[loop]) == numbers[loopOther]) //how do I parse this?
{
lottCount++;
}else if (Integer.valueOf(userArray[loop]) == bonus)
{
bonus = true;
}
}//for
}//for main
Parse Like this :
for (int loop = 0; loop < userArray.length; loop++)
{
for (int loopOther = 0; loopOther < numbers.length; loopOther++)
{
if (Integer.parseInt(userArray[loop]) == numbers[loopOther])
{
lottCount++;
}else if (userArray[loop] == bonus)
{
bonus = true;
}
}
}
Android 2.3.3
I have written a program for calculating the LCM for more than 2 numbers, and it worked for me. I thought of sharing it, so that it might come in handy for those who are looking for it. This may not be the best solution, but, i did it according to my requirement. You can modify it to your need.
I have hard coded the input, and also my program uses ArrayLists to do the operations. You might want to change these.
Pre-Requisites ::: 1. Calculation of PrimeNumbers for the range of inputs.
public class PlusMinusActivity extends Activity implements OnClickListener {
/** Called when the activity is first created. */
EditText edtxtExpression;
Button btnLCM, btnGCD;
ArrayList<String> alPrimes = new ArrayList<String>(); // Contains List of Prime Numbers
ArrayList<String> alNumbers = new ArrayList<String>(); // Contains the input => Numbers for which LCM is to be determined
ArrayList<String> alResult = new ArrayList<String>(); // Contains the numbers that make up the LCM
String strExp = ""; // Temporary String to display the result
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
edtxtExpression = (EditText)findViewById(R.id.edtxtExpression);
btnLCM = (Button)findViewById(R.id.btnLCM);
btnGCD = (Button) findViewById(R.id.btnGCD);
btnLCM.setOnClickListener(this);
btnGCD.setOnClickListener(this);
addData();
strExp = alNumbers.toString();
System.out.println("strExp Value is ::: "+strExp);
}
private void addData() {
// TODO Auto-generated method stub
//alPrimes.add(String.valueOf(1));
alPrimes.add(String.valueOf(2));
alPrimes.add(String.valueOf(3));
alPrimes.add(String.valueOf(5));
alPrimes.add(String.valueOf(7));
alPrimes.add(String.valueOf(9));
alPrimes.add(String.valueOf(11));
alPrimes.add(String.valueOf(13));
alPrimes.add(String.valueOf(17));
alPrimes.add(String.valueOf(19));
alPrimes.add(String.valueOf(23));
alPrimes.add(String.valueOf(29));
alNumbers.add(String.valueOf(1));
alNumbers.add(String.valueOf(5));
alNumbers.add(String.valueOf(7));
alNumbers.add(String.valueOf(9));
System.out.println("alPrimes ::: "+alPrimes.toString());
System.out.println("alNumbers ::: "+alNumbers.toString());
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.btnLCM:
calculateLCM();
break;
case R.id.btnGCD:
calculateGCD();
break;
default:
break;
}
}
// Calculates LCM
private void calculateLCM() {
// TODO Auto-generated method stub
int i=0, count=0;
while(i < alPrimes.size())
{
count = 0;
int p = Integer.parseInt(alPrimes.get(i)); // Getting the element from PrimeNumbers List
System.out.println("Prime Number ::: "+p);
int j=0;
while(j < alNumbers.size())
{
int n = Integer.parseInt(alNumbers.get(j)); // Getting the number from Input List
System.out.println("Number ::: "+n);
if(n % p == 0 && n != 1)
{
count++; // Counts the number of integers that gets divided (% = 0) by that particular prime number
System.out.println("Count :::"+count);
}
j++;
}
if(count >= 2) // If two or more numbers, gets divided, then we do the division
{
alResult.add(String.valueOf(p)); // adding the prime number to Result list
System.out.println("Result ::: "+alResult.toString());
j=0;
while(j < alNumbers.size())
{
int n = Integer.parseInt(alNumbers.get(j));
System.out.println("Number ::: "+n);
if(n % p == 0)
{
int result = n/p;
System.out.println("Temp Result ::: "+result);
alNumbers.remove(j); // Replace the element by the result
System.out.println("After Removing ::: "+alNumbers.toString());
alNumbers.add(j, String.valueOf(result));
System.out.println("After Adding ::: "+alNumbers.toString());
}
j++;
}
i = -1; // iterate the Input list from the start
}
else if(count == 0 || count == 1)
{
boolean allPrimes = checkAllPrimes();
if(allPrimes)
{
break;
}
}
i++;
}
calculateResult();
}
// Calculates the result
private void calculateResult() {
// TODO Auto-generated method stub
int i=0;
while(i < alNumbers.size())
{
alResult.add(alNumbers.get(i));
i++;
}
int result = 1;
i=0;
while(i < alResult.size())
{
result *= Integer.parseInt(alResult.get(i));
i++;
}
edtxtExpression.setText("LCM of "+strExp+" is ::: "+result);
}
// Checks whether the elements in the ArrayList are all prime numbers
// returns true if all are prime
//
private boolean checkAllPrimes() {
// TODO Auto-generated method stub
int i=0;
boolean areAllPrimes = true;
while(i < alNumbers.size())
{
int n = Integer.parseInt(alNumbers.get(i));
if(! (alPrimes.contains(n) || n == 1))
{
areAllPrimes = false;
break;
}
i++;
}
return areAllPrimes;
}
private void calculateGCD() {
// TODO Auto-generated method stub
}
}
For the following input :::
alNumbers.add(String.valueOf(10));
alNumbers.add(String.valueOf(15));
alNumbers.add(String.valueOf(20));
alNumbers.add(String.valueOf(25));
For the following input :::
alNumbers.add(String.valueOf(10));
alNumbers.add(String.valueOf(15));
alNumbers.add(String.valueOf(20));
alNumbers.add(String.valueOf(25));
alNumbers.add(String.valueOf(110));
alNumbers.add(String.valueOf(130));
I am very new to Android and Java as well. So, if this is not a good solution, please don't mind.
Hope it helps...
You could probaly simplify your code using this idea:
static int ggt(int a, int b)
{
if (b == 0)
return a;
return ggt(b, a % b);
}
static void Main(string[] args)
{
int lcm = 1;
foreach(int x in new int[] { 1,5,7,9 })
lcm = x * lcm / ggt(x, lcm);
Console.WriteLine("{0}", lcm);
}
Syntax is c#, but hopefully readable enough. 'ggt' ist the german abbrevation for 'gcd' (greatest common divisor)
//LCM of range of numbers using JAVA
n=s.nextInt();//Reading range value
for(i=0;i<n;i++)
{
a[i]=s.nextInt(); //reading list of numbers
}
Arrays.sort(a,0,n); //Sorting the numbers
k=a[n-1]; //Assigning biggest value in the sorted array to k
j=1;
l=k;
for(i=0;i<n;i++)
{
if(k%a[i]==0)
{
continue; //checking whether all the elements are divisible by k
}
else
{
j=j+1;
k=l*j;//multiples of highest element i.e k in the sorted array
i=-1;//Assigning -1 to i, so as to check whether all the elements
//in the array are divisible by k or not from the beginning
}
}
System.out.println("LCM of range of numbers:"+k);
Input:
6
2 4 6 8 9 3
Output:
LCM of range of numbers:72
I am working on a android app and I have an EditText where user can input numbers. I want to format the number using different currency formats (say ##,##,###) and I want to do it on the fly, ie when user enter each digit(not when enter is pressed). I googled around, and came across TextWatcher which I first found promising, but it turned out to be an absolute pain. I am debugging my code on a HTC Desire phone which only has a soft keyboard.
Now I want to get a callback when user press numbers (0 to 9) , del (backspace) key and enter key. From my testing I found these (atleast on my phone)
1) editText onKeyListener is called
when user presses del or enter key.
When user presses enter, onKey
function is called twice for one enter
(which I believe is for ACTION_UP and
ACTION_DOWN). When user presses del,
onKey is called once (only for
ACTION_DOWN) which I dont know why.
onKey is never called when user
presses any digits(0 to 9) which too I
cant understand.
2) TextWatchers 3 callback functions
are called (beforeTextChanged,
onTextChanged, afterTextChanged)
whenever user presses any number (0 to
9) key . So I thought by using
TextWatcher and onKeyListener together
I can get all callbacks I need.
Now my questions are these..
1) First in my HTC soft keyboard there
is a key (a keyboard symbol with a
down arrow) and when I click on it
keyboard is resigned without giving
any callback. I still cant believe
android letting user to edit a field
and resign without letting program to
process (save) the edit. Now my
editText is showing one value and my
object has another value (I am saving
user edits on enter, and handling back
press on keyboard by reseting
editText value with the value in the
object , but I have no answer to this
keyboard down key).
2) Second, I want to format the number
after user entered the new digit. Say
I already have 123 on editText and
user entered pressed 4, I want my
editText to display 1,234. I get full
number on onTextChanged() and
afterTextChanged() and I can format
the number and put it back to
editText in any of these callback.
Which one should I use? Which is the
best practice?
3) Third one is the most crucial
problem. When app start I put the
current object value in the editText.
Say I put 123 on onResume(), and when
user enter a digit (say 4) I want it
to be 1234. But on my onTextChanged
callback what I am getting is 4123. When
I press one more key (say 5) I am
getting 45123. So for user inputs
editText cursor are pointing to end of
the text. But when value is set by
hand, editText cursor dont seems to be
updating. I believe I have to do
something in textWatcher callbacks but
I dont know what I should do.
I am posting my code below.
public class AppHome extends AppBaseActivity {
private EditText ed = null;
private NumberFormat amountFormatter = null;
private boolean isUserInput = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_home_screen);
ed = (EditText)findViewById(R.id.main_amount_textfield);
amountFormatter = new DecimalFormat("##,##,###");
ed.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN)
return true;
String strippedAmount = ed.getText().toString().replace(",", "");
if(keyCode == KeyEvent.KEYCODE_DEL){
//delete pressed, strip number of comas and then delete least significant digit.
strippedAmount = strippedAmount.substring(0, strippedAmount.length() - 1);
int amountNumeral = 0;
try{
amountNumeral = Integer.parseInt(strippedAmount);
} catch(NumberFormatException e){
}
myObject.amount = amountNumeral;
isUserInput = false;
setFormattedAmount(amountNumeral,ed.getId());
}else if(keyCode == KeyEvent.KEYCODE_ENTER){
//enter pressed, save edits and resign keyboard
int amountNumeral = 0;
try{
amountNumeral = Integer.parseInt(strippedAmount);
} catch(NumberFormatException e){
}
myObject.amount = amountNumeral;
isUserInput = false;
setFormattedAmount(myObject.amount,ed.getId());
//save edits
save();
//resign keyboard..
InputMethodManager in = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
in.hideSoftInputFromWindow(AppHome.this.getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
}
return true;
}
});
TextWatcher inputTextWatcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
if(isUserInput == false){
//textWatcher is recursive. When editText value is changed from code textWatcher callback gets called. So this variable acts as a flag which tells whether change is user generated or not..Possibly buggy code..:(
isUserInput = true;
return;
}
String strippedAmount = ed.getText().toString().replace(",", "");
int amountNumeral = 0;
try{
amountNumeral = Integer.parseInt(strippedAmount);
} catch(NumberFormatException e){
}
isUserInput = false;
setFormattedAmount(amountNumeral,ed.getId());
}
public void beforeTextChanged(CharSequence s, int start, int count, int after){
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
};
ed.addTextChangedListener(inputTextWatcher);
}//end of onCreate...
public void setFormattedAmount(Integer amount, Integer inputBoxId){
double amountValue = 0;
String textString =null;
TextView amountInputBox = (TextView) findViewById(inputBoxId);
amountValue = Double.parseDouble(Integer.toString(amount));
textString = amountFormatter.format(amountValue).toString();
amountInputBox.setText(textString);
}
}
I know it is a big question, but I am working on this same problem for 2 days. I am new to android and still cant believe that there is no easy way to process textEdit data on the fly (I done the same on iphone with ease). Thanks all
EDIT: after using input filter
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String strippedAmount = dest.toString() + source;
strippedAmount = strippedAmount.replace(",", "");
int amountNumeral = 0;
try{
amountNumeral = Integer.parseInt(strippedAmount);
} catch(NumberFormatException e){
}
return amountFormatter.format(amountNumeral).toString();
}
};
ed.setFilters(new InputFilter[]{filter});
When app starts I am putting 1,234 on the editText
myObject.amount = 1234;
ed.setText(amountFormatter.format(myObject.amount).toString());
Then when user clicks the editText, keyboard pops up, and say user enters digit 6
I am getting : 61234 I want :
12346
Well, after much head banging, I found a work around for cursor position problem..I dont know whether it is the correct way, But I got it working..
TextWatcher inputTextWatcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
if(isUserInput == false){
//textWatcher is recursive. When editText value is changed from code textWatcher callback gets called. So this variable acts as a flag which tells whether change is user generated or not..Possibly buggy code..:(
isUserInput = true;
ed.setSelection(ed.getText().length());
return;
}
String strippedAmount = ed.getText().toString().replace(",", "");
int amountNumeral = 0;
try{
amountNumeral = Integer.parseInt(strippedAmount);
} catch(NumberFormatException e){
}
isUserInput = false;
setFormattedAmount(amountNumeral,ed.getId());
}
public void beforeTextChanged(CharSequence s, int start, int count, int after){
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
};
ed.addTextChangedListener(inputTextWatcher);
ed.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int length = ed.getText().length();
ed.setCursorVisible(true);
ed.setSelection(length);
}
});
ed.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_UP)
return true;
String strippedAmount = ed.getText().toString().replace(",", "");
if(keyCode == KeyEvent.KEYCODE_DEL){
//delete pressed, strip number of comas and then delete least significant digit.
strippedAmount = strippedAmount.substring(0, strippedAmount.length() - 1);
int amountNumeral = 0;
try{
amountNumeral = Integer.parseInt(strippedAmount);
} catch(NumberFormatException e){
}
isUserInput = false;
setFormattedAmount(amountNumeral,ed.getId());
return true;
}else if(keyCode == KeyEvent.KEYCODE_ENTER){
//enter pressed, save edits and resign keyboard
int amountNumeral = 0;
try{
amountNumeral = Integer.parseInt(strippedAmount);
} catch(NumberFormatException e){
}
isUserInput = false;
setFormattedAmount(amountNumeral,ed.getId());
//save edits
//resign keyboard..
InputMethodManager in = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
in.hideSoftInputFromWindow(AppHome.this.getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
return true;
}
return false;
}
});
What I have done is on onClick() of editText, I forcefully put the cursor at the end of the current EditText text, and I have done the same when user pressed any digit. Hope it helps someone..Thanks for everyone who tried to help.
For Masked input, you can subclass InputFilter
Below is a sample InputFilter subclass, which capitalizes all lower case letters:
/**
* This filter will capitalize all the lower case letters that are added
* through edits.
*/
public static class AllCaps implements 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.isLowerCase(source.charAt(i))) {
char[] v = new char[end - start];
TextUtils.getChars(source, start, end, v, 0);
String s = new String(v).toUpperCase();
if (source instanceof Spanned) {
SpannableString sp = new SpannableString(s);
TextUtils.copySpansFrom((Spanned) source,
start, end, null, sp, 0);
return sp;
} else {
return s;
}
}
}
return null; // keep original
}
}
The above code is taken from Android's implementation of InputFilter
After several hours of working I made a phone input mask.
For istance, after entering "123456" it converts it to "+1 (234) 56". After deleting of any symbol from any position a cursor moves to a right position, not to a beginning or ending.
In Activity:
etPhone.addTextChangedListener(new PhoneWatcher(etPhone));
In class:
private class PhoneWatcher implements TextWatcher {
private static final String PHONE_MASK = "+# (###) ###-##-##";
private final char[] PHONE_MASK_ARRAY = PHONE_MASK.toCharArray();
private boolean isInTextChanged;
private boolean isInAfterTextChanged;
private EditText editText;
private int shiftCursor;
private String text;
private int cursor;
public PhoneWatcher(EditText editText) {
super();
this.editText = editText;
isInTextChanged = false;
isInAfterTextChanged = false;
}
#Override
public synchronized void beforeTextChanged(CharSequence s, int start, int count, int after) {
shiftCursor = after - count;
}
#Override
public synchronized void onTextChanged(CharSequence s, int start, int before, int count) {
if (!isInTextChanged) {
isInTextChanged = true;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char symbol = s.charAt(i);
if (symbol >= '0' && symbol <= '9')
sb.append(symbol);
}
String digits = sb.toString();
sb.setLength(0);
int j = 0;
for (int i = 0; i < digits.length(); i++) {
char digit = digits.charAt(i);
while (j < PHONE_MASK_ARRAY.length) {
if (PHONE_MASK_ARRAY[j] == '#') {
sb.append(digit);
j++;
break;
} else {
sb.append(PHONE_MASK_ARRAY[j]);
j++;
}
}
}
cursor = editText.getSelectionStart();
text = sb.toString();
if (shiftCursor > 0) {
if (cursor > text.length())
cursor = text.length();
else {
while (cursor < PHONE_MASK_ARRAY.length && PHONE_MASK_ARRAY[cursor - 1] != '#') {
cursor++;
}
}
} else if (shiftCursor < 0) {
while (cursor > 0 && PHONE_MASK_ARRAY[cursor - 1] != '#') {
cursor--;
}
}
}
}
public synchronized void afterTextChanged(Editable s) {
if (!isInAfterTextChanged) {
isInAfterTextChanged = true;
editText.setText(text);
editText.setSelection(cursor);
isInTextChanged = false;
isInAfterTextChanged = false;
}
}
}