I have a simple ListActivity that uses the SimpleCursorAdapter. I allow users to change one of the values using an EditText. I perform simple validation to make sure that the number entered is less than 100. If the user entered value fails validation, I want to put the old value back.
I've tried a few different ways. My current approach is to requery it out of the database, but this isn't working. I'm always getting the value associated with the last entry in the ListActivity, regardless of which one was actually changed. I noticed in LogCat that onTextChanged and afterTextChanged are firing multiple times for each row in the ListActivity and not just the one that changed.
Here's the code:
public class MySimpleCursorAdapter extends SimpleCursorAdapter {
Context lcontext;
boolean changed;
String lastval;
private PortfolioData pfdata;
public MySimpleCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
lcontext = context;
}
#Override
public View getView(final int pos, View v, ViewGroup parent) {
v = super.getView(pos, v, parent);
final EditText et = (EditText) v.findViewById(R.id.classpercentage);
final TextView tv = (TextView) v.findViewById(R.id._id);
et.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
Log.d("TEST", "In afterTextChanged s=" + s.toString() + " "
+ tv.getText() + " POS = " + Integer.toString(pos));
lastval = tv.getText().toString();
if (changed == true) {
String enteredValue = s.toString();
if (checkNullValues(enteredValue)) {
if (Float.parseFloat(enteredValue.trim()) > 100.0f) {
AlertDialog.Builder builder = new AlertDialog.Builder(
lcontext);
builder.setMessage("Percentage Value should be Less than 100");
builder.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface arg0, int arg1) {
String sql = "select c.percentage as PERCENTAGE " +
"from asset_classes c WHERE c._id = " + lastval + ";";
pfdata = new PortfolioData(lcontext);
SQLiteDatabase db = pfdata.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, null);
if (cursor != null)
{
cursor.moveToFirst();
et.setText(cursor.getString(0));
}
cursor.close();
pfdata.close();
}
});
// End of the Alert
if (changed == true)
{
builder.show();
}
}
}
changed = false;
}
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// Log.d("TEST", "In beforeTextChanged start=" +
// Integer.toString(start) +" count="+ Integer.toString(count) +
// " after=" + Integer.toString(after) + " s=" + s + " " + tv);
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
Log.d("TEST", "In onTextChanged start=" +
Integer.toString(start) + " count=" + Integer.toString(count)
+ " before=" + Integer.toString(before) + " s=" + s + " " +
tv);
changed = true;
}
});
return v;
}
}
I would really appreciate a fresh perspective on this. As always, thanks in advance.
Try to use an onFocusChangeListener. When it gets focus, save the current text into field of the class.
Something like:
String oldText - Would be your old text field.
Then you do:
et.setOnFocusChangeListener(new OnFocusChangeListener()) {
#Override
public void onFocusChange(View whatever, boolean hasFocus) {
if (hasFocus) {
//code
} else {
//code or maybe empty
}
}
}
Then if the number is > 100 you just get the oldText value and put in the EditText.
I think you're not aware of recycling in ListView. Having 1000 rows in Cursor there is only 10-20 (depends on screen size) row views created.
Don't store data in view!
At beggining I recommend you to read http://commonsware.com/Android/excerpt.pdf
Related
I'm showing contacts in a ListView and trying to filter the same list using an EditText. But when I type something, filtering is not happening, though the typed text is coming in Logcat.
Here is my onCreate:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_invite);
EditText filterText = findViewById(R.id.search_box);
filterText.addTextChangedListener(filterTextWatcher);
// The contacts from the contacts content provider is stored in this cursor
mMatrixCursor = new MatrixCursor(new String[]{"_id", "name", "details"});
// Adapter to set data in the listview
mAdapter = new SimpleCursorAdapter(getBaseContext(),
R.layout.contact_layout, null, new String[]{"name", "details"}, new int[]{R.id.tv_name, R.id.tv_details}, 0);
// Getting reference to listview
ListView lstContacts = findViewById(R.id.lst_contacts);
// Setting the adapter to listview
lstContacts.setAdapter(mAdapter);
// Creating an AsyncTask object to retrieve and load listview with contacts
ListViewContactsLoader listViewContactsLoader = new ListViewContactsLoader();
// Starting the AsyncTask process to retrieve and load listview with contacts
listViewContactsLoader.execute();
}
This is my TextWatcher:
private TextWatcher filterTextWatcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
Log.d(TAG, "onTextChanged: " + s);
mAdapter.getFilter().filter(s.toString());
}
};
What am I doing wrong here? Can someone help?
EDIT: Adding my ListViewContactsLoader.
private class ListViewContactsLoader extends AsyncTask<Void, Void, Cursor> {
#Override
protected Cursor doInBackground(Void... params) {
Uri contactsUri = ContactsContract.Contacts.CONTENT_URI;
// Querying the table ContactsContract.Contacts to retrieve all the contacts
Cursor contactsCursor = getContentResolver().query(contactsUri,
null, null, null,
ContactsContract.Contacts.DISPLAY_NAME + " ASC ");
if (contactsCursor.moveToFirst()) {
do {
long contactId = contactsCursor.getLong(contactsCursor
.getColumnIndex("_ID"));
Uri dataUri = ContactsContract.Data.CONTENT_URI;
// Querying the table ContactsContract.Data to retrieve individual items like
// home phone, mobile phone etc corresponding to each contact
Cursor dataCursor = getContentResolver().query(dataUri,
null,
ContactsContract.Data.CONTACT_ID + "=" + contactId,
null, null);
String displayName = "";
String mobilePhone = "";
if (dataCursor.moveToFirst()) {
// Getting Display Name
displayName = dataCursor
.getString(dataCursor
.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
do {
// Getting Phone numbers
if (dataCursor
.getString(
dataCursor
.getColumnIndex("mimetype"))
.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
switch (dataCursor.getInt(dataCursor
.getColumnIndex("data2"))) {
case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE:
mobilePhone = dataCursor
.getString(dataCursor
.getColumnIndex("data1"));
break;
}
}
} while (dataCursor.moveToNext());
String details = "";
// Concatenating various information to single string
if (mobilePhone != null && !mobilePhone.equals(""))
details = mobilePhone + "\n";
// Adding id, display name, path to photo and other details to cursor
mMatrixCursor.addRow(new Object[]{
Long.toString(contactId), displayName, details});
}
} while (contactsCursor.moveToNext());
}
return mMatrixCursor;
}
#Override
protected void onPostExecute(Cursor result) {
// Setting the cursor containing contacts to listview
mAdapter.swapCursor(result);
Log.d(TAG, "onPostExecute: " + result);
}
}
You need to reset the ListViewadapter also after adding the text in EditText.
searchEditText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
System.out.println("Text [" + s + "] - Start [" + start + "] - Before [" + before + "] - Count [" + count + "]");
if (count < before) {
listAdapter.resetData(); // MARK: Resetting adapter here
}
listAdapter.getFilter().filter(s);
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
});
Note: resetData() is custom method in which you have to replace the List data with new List Data that appears after applying the Filter. Then finally notify the Adapter.
I want to show text in text view if condition is satisfied with equal method in Text-watcher.I have a string variable used in a method, and I would like to compare the EditText value to that variable and text view is update and if not then display the string with wrong code.But ui is not updated in TextWatcher.
Here is my code
private final TextWatcher vesselWatcher = new TextWatcher()
{
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
Log.e("beforeTextChanged ", " here !!! ");
}
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
public void afterTextChanged(Editable s)
{
if (s.length() == 0)
{
txtVesselName.setVisibility(View.GONE);
Log.e("afterTextChanged View ", " is gone !!!");
btnPost.setClickable(false);
btnPost.setEnabled(false);
Log.e("afterTextChanged ", " btnPost.setEnabled(false); !!!");
}
else
{
if(notations.contains(etxtVesselCode.getText().toString().trim()))
{
Log.e("AAA strNotations ", " Match !!! = " + etxtVesselCode.getText().toString().trim());
String notations = etxtVesselCode.getText().toString().trim();
SQLiteDatabase db = dbhelper.getWritableDatabase();
Cursor cursor = db.rawQuery("select * from VesselList where Notation " + "= ? ", new String[]{notations});
if (cursor.moveToFirst())
{
do
{
strVesselsTypeName = cursor.getString(cursor.getColumnIndex("VesselsTypeName"));
Log.e("strVesselsTypeName ", "= " + strVesselsTypeName );
SharedPreferences sp;
sp = getApplicationContext().getSharedPreferences(MyPREFERENCES, 0);
SharedPreferences.Editor e = sp.edit();
e.putString("xyz", etxtVesselCode.getText().toString().trim());
e.commit();
Log.e("Match","!!!!");
txtVesselName.setText(strVesselsTypeName);
btnPost.setClickable(true);
btnPost.setEnabled(true);
Log.e("afterTextChanged ", " btnPost.setEnabled(true); !!!");
} while (cursor.moveToNext());
} db.close();
}
else
{
Log.e("Not "," Match !!");
txtVesselName.setText("Wrong Code !!");
btnPost.setClickable(false);
btnPost.setEnabled(false);
}
}
}
};
Make txtVesselName TextView VISIBLE again after calling txtVesselName.txtVesselName when if condition is true:
txtVesselName. setVisibility(View.VISIBLE);
Folks.
I've designed a currency exchange application in which i got all exchange rates once at application start and create own SQLite DB then easily pull them into the converter interface. I've designed the change to be done in my Edittext onTextChanged listener with a textwatcher and all works perfectly. I've also have a listview in the same activity for Favorite exchange rates and its also has to be calculated every time the edittext changes. My main problem is that I've got a slow performance and freeze issues in my application. I've tried to implement Asynctask to process the calculations but it didn't help me and i still get the performance issues. Hereunder my code for your reference. Please Advice !!
Text Watcher :
valval.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(final Editable s)
{
Calculate();
}
});
Calculate :
private void Calculate()
{
curs = mDb.query(MyDbHelper.TABLE_NAME, columns, MyDbHelper.COL_Common
+ "=" + "?", new String[] { From[xxxto] + From[xxxfrom] },
null, null, null);
cursD = mDb.query(MyDbHelper.TABLE_NAME, columns, MyDbHelper.COL_Common
+ "=" + "?", new String[] { From[xxxfrom] + From[xxxto] },
null, null, null);
curs.moveToFirst();
cursD.moveToFirst();
double selection = curs.getDouble(curs
.getColumnIndex(MyDbHelper.COL_Currone));
double selection2 = cursD.getDouble(cursD
.getColumnIndex(MyDbHelper.COL_Currone));
Long myNum = Long.parseLong(valval.getText().toString().trim());
double myNum3 = Double.parseDouble(new DecimalFormat("#.######").format(myNum * selection2));
valval2.setText(String.valueOf(myNum3));
Cursor B = mDb.query(MyDbHelper.TABLE_NAME, columns,
MyDbHelper.COL_CurrFavor + " LIKE ? And "
+ MyDbHelper.COL_Currsecond + " LIKE ?", new String[] {
"YES", "EUR" }, null, null, null);
for (int s = 0; s < B.getCount() - 1; s++)
{
B.moveToPosition(s);
String ZVZV = B.getString(0);
int BSBS = B.getInt(9);
Cursor curcur = mDb.query(MyDbHelper.TABLE_NAME, columns, MyDbHelper.COL_Common
+ "=" + "?", new String[] { From[xxxfrom] + From[BSBS-1] },
null, null, null);
curcur.moveToFirst();
double calcal = curcur.getDouble(6);
ContentValues args = new ContentValues();
double formattedNumber = Double.parseDouble(new DecimalFormat("#.######").format(myNum * calcal));
args.put(MyDbHelper.COL_Currsum,formattedNumber );
mDb.update(MyDbHelper.TABLE_NAME, args, "_id =" + ZVZV, null);
}
cursm.requery();
}
What I can imagine of from your description is, you're most likely having 2 EditTexts, where user only input to first, and you will sync the converted value for second, is this true? If it is, may I suggest you not to perform the operation right after every text change?
You may want to do Calculate() only after user finishes his input, perhaps one second delay of calculation is acceptable?
// Declare these as class variable
private Handler handler = new Handler();
private Runnable calculateRunnable = new Runnable() {
public void run() {
Calculate();
}
}
As for your TextWatcher, change this
public void afterTextChanged(final Editable s) {
handler.removeCallbacks(calculateRunnable);
handler.postDelayed(calculateRunnable, 1000);
}
I have have checked every problem here in relation to my own. The codes are totally different in the one I'm using IMHO. I have coded everything up to this point. I've read that in deleting the data in the database is just by passing the position, the problem is the auto-generated id in the database is not the same with the position being passed. All I'm trying to achieve is after deleting the item in list view, the item in the database will also be deleted.
The code from database in getting all the data:
public ArrayList<String> getData() {
String[] columns = new String[] { KEY_ID, KEY_CLIENT_NAME,
KEY_PRODUCT_NAME, KEY_SUPPLIER_PRICE, KEY_BS_PRICE, KEY_QTY,
KEY_TOTAL_AMOUNT };
Cursor c = sql.query(DB_TABLE, columns, null, null, null, null, null);
ArrayList<String> result = new ArrayList<String>();
int id = c.getColumnIndex(KEY_ID);
int cName = c.getColumnIndex(KEY_CLIENT_NAME);
int pName = c.getColumnIndex(KEY_PRODUCT_NAME);
int suppPrice = c.getColumnIndex(KEY_SUPPLIER_PRICE);
int bsPrice = c.getColumnIndex(KEY_BS_PRICE);
int qty = c.getColumnIndex(KEY_QTY);
int totalAmount = c.getColumnIndex(KEY_TOTAL_AMOUNT);
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
result.add("ID: " + c.getInt(id) + "\n" + "CLIENT NAME: "
+ c.getString(cName) + "\n" + "PRODUCT NAME: "
+ c.getString(pName) + "\n" + "SUPPLIER PRICE: "
+ c.getString(suppPrice) + "\n" + "RETAIL PRICE: "
+ c.getString(bsPrice) + "\n" + "QUANTITIY: "
+ c.getString(qty) + "\n" + "TOTAL: "
+ c.getString(totalAmount) + "\n");
}
return result;
}
The code in retrieving the data:
sql = new SQLDatabase(this);
sql.open();
ArrayList<String> data = sql.getData();
sql.close();
The code the the deletion should be executed:
protected void onListItemClick(View view, int position, long id) {
Log.i("TAG", "onListItemClick id=" + position + id);
AlertDialog.Builder builder = new AlertDialog.Builder( Delete.this);
builder.setMessage("WHAT DO YOU WANT TO DO?");
builder.setNegativeButton("DELETE", new
DialogInterface.OnClickListener() {
#Override public void onClick(DialogInterface dialog, int which) { }
// DELETE CODE HERE
});
builder.setNeutralButton("CANCEL", new
DialogInterface.OnClickListener() {
#Override public void onClick(DialogInterface dialog, int which) { }
// DO NOTHING
});
builder.setCancelable(false); AlertDialog alertdialog =
builder.create(); alertdialog.show();
}
Add Tag to the column you inserted and use same tag value while delete operation in both list and DB.
I need to update my ListView according to the userinput in SearchDialog. For this I need to set a TextWatcher for SearchDialog. How to do this?
you can do it as below:
yourEditText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// process new items for list view
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// push new items into adapter and call notifyDataSetChanged() on it
}
});
Here is what I did actually a few months ago to achieve the same thing that you need. I had to sort my list view depending on user input while I was populating the list view from sqlite database with sqlite statements. Not sure if it's the best way,but in my situation it works perfectly.
So on my EditText field I add this (which name is searchBar) :
searchString = "";
searchBar.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void afterTextChanged(Editable s) {
// this is where actually I was changing the sqlite statement which sort my list view
names.clear();
categories.clear();
paths.clear();
count.clear();
cardId.clear();
myCardID.clear();
searchString = s.toString();
Log.e("","searchString : "+searchString);
sqlQuery = getSqlStatement(sort, collId, ascDesc,searchString);
Log.e(""," sqlquery : "+sqlQuery);
sqlQuery = getSqlStatement(sort, collId, ascDesc, searchString);
Cursor cursor = userDbHelper.executeSQLQuery(sqlQuery);
if (cursor.getCount() == 0) {
noCards.setVisibility(View.VISIBLE);
} else if (cursor.getCount() > 0) {
noCards.setVisibility(View.GONE);
for (cursor.move(0); cursor.moveToNext(); cursor.isAfterLast()) {
objectId = Integer.parseInt(cursor.getString(cursor.getColumnIndex("objectId")));
cardId.add(objectId);
title = cursor.getString(cursor.getColumnIndex("title"));
catTitle = cursor.getString(cursor.getColumnIndex("catTitle"));
if(extra!=0 && sort!=3){
tags.setText(catTitle);
}
int repeats = Integer.parseInt(cursor.getString(cursor.getColumnIndex("repeatsCount")));
count.add(repeats);
String cardsm = "SELECT objectId FROM cardmedias " +
"WHERE cardId="+
objectId +
" AND mediaType=" +
5012;
Cursor cardsM = userDbHelper.executeSQLQuery(cardsm);
if (cardsM.getCount() == 0) {
String defCard = "SELECT objectId FROM collectionmedias " +
"WHERE collectionId="+
collId +
" AND mediaType=" +
3003;
Cursor getDefCard = userDbHelper.executeSQLQuery(defCard);
getDefCard.moveToFirst();
objectID = Integer.parseInt(getDefCard.getString(getDefCard
.getColumnIndex("objectId")));
String filename = "mediacollection-"+objectID;
if(storageID==1){
path = RPCCommunicator.getImagePathFromInternalStorage(servername, userId, filename, getApplicationContext());
} else if(storageID==2){
path = RPCCommunicator.getImagePathFromExternalStorage(servername, userId, filename);
}
hm.put(objectID, path);
path = hm.get(objectID);
paths.add(path);
names.add(title);
categories.add(catTitle);
} else if (cardsM.getCount() > 0) {
for (cardsM.move(0); cardsM.moveToNext(); cardsM.isAfterLast()) {
objectID = Integer.parseInt(cardsM.getString(cardsM
.getColumnIndex("objectId")));
String filename = "mediacard-"+objectID;
if(storageID==1){
path = RPCCommunicator.getImagePathFromInternalStorage(servername, userId, filename, getApplicationContext());
} else if(storageID==2){
path = RPCCommunicator.getImagePathFromExternalStorage(servername, userId, filename);
}
hm.put(objectID, path);
path = hm.get(objectID);
paths.add(path);
names.add(title);
categories.add(catTitle);
}
}
cardsM.close();
}
}
cursor.close();
// don't forget to add this!!!
adapter.notifyDataSetChanged();
}
});
And in my onCreate() actually I'm doing the same thing as in TextWatcher to sort my list view, so I think you can implement the same logic as I did.
If you have any problems to do that, just paste some of your code so we can help you! : )
Hope this helps!