I have an EditText control on top of a ListView control in my layout. This layout is loaded into a Fragment derived class. I have also associated a TextWatcher interface with the EditText control so that each time text is entered into the EditText, the ListView filters the data based on the input text. The ListView's adapter implements Filterable behaviour by overriding GetFilter. The Fragment class that I am describing is part of a FragmentTabHost control which in turn loads 3 tabs at runtime.
Filtering the list based on what I enter in the EditText control works correctly. The issue I am facing is that when I click on some other Tab and then come back to my Tab which has this EditText with a TextWatcher, it is not clearing the previously entered text. I also notice that the filtered list is now empty. Once I manually remove the previous text from the EditText, the List re-appears with the old data.
Please let me know how to reset the filter and clear the text in the EditText control when I tab out of the current fragment. Basically I do not want the EditText control and the Filter to retain the old context each time I click on the Fragment. I have pasted sections of the code from the Fragment and List Adapter.
Thanks
Balaji
public class Favourites extends Fragment implements LoaderCallbacks<Cursor> {
ListView listView1;
ArrayList<FavouriteSearchResults> searchResults;
FavouritesBaseAdapter customAdapter;
private ProgressBar progressBar;
private EditText editText;
final int MENU_MAKE_CALL = 100;
final int MENU_SEND_MESSAGE = 101;
final int MENU_SEND_DIAL = 102;
final int MENU_COPY_CLIP = 103;
final int MENU_SEND_NUMBER = 104;
final int MENU_VIEW_CONTACT = 105;
private static final int LIST_ID = 1004;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setHasOptionsMenu(true); //Call this so that onCreateOptionsMenu is called
}
private class MyFocusChangeListener implements OnFocusChangeListener {
Context mContext;
public MyFocusChangeListener(Context context) {
mContext = context;
}
public void onFocusChange(View v, boolean hasFocus){
if(v.getId() == R.id.txtSearch && !hasFocus) {
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.favourites, container, false);
editText = (EditText) v.findViewById(R.id.txtSearch);
OnFocusChangeListener ofcListener = new MyFocusChangeListener(getActivity());
editText.setOnFocusChangeListener(ofcListener);
editText.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
customAdapter.getFilter().filter(cs);
}
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
});
#Override
public Filter getFilter() {
// TODO Auto-generated method stub
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults results = new FilterResults();
if(charSequence == null || charSequence.length() == 0) {
results.values = searchArrayList; //Return original list if search is cleared
results.count = searchArrayList.size();
}
else {
ArrayList<FavouriteSearchResults> tempResults = new ArrayList<FavouriteSearchResults>();
for(int i = 0;i < searchArrayList.size();i++) {
FavouriteSearchResults favResults = searchArrayList.get(i);
String sContactName = favResults.GetName();
String sId = favResults.GetId();
String searchChar = charSequence.toString();
if (sContactName.toLowerCase().contains(searchChar.toLowerCase())) {
FavouriteSearchResults newFavResults = new FavouriteSearchResults(sContactName);
newFavResults.SetId(sId);
tempResults.add(newFavResults);
}
}
results.values = tempResults;
results.count = tempResults.size();
}
return results;
}
#Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
//Set the filtered list into our copy list
searchFilteredArrayList = (ArrayList<FavouriteSearchResults>)filterResults.values;
notifyDataSetChanged();
}
};
First of all, I would point out that your getFilter() method is extremely not efficient. It creates new Filter every time user types/deletes a letter. This is quite stressful for the garbage collector. Instead, I would suggest creating Filter instance in your fragment and return this instance in getFilter() method.
Also, if you really want to clear edit text and reset filter - I would override onDestroyView method in your fragment implementation and do that stuff there.
#Override
public void onDestroyView() {
editText.setText("");
editText.setOnFocusChangeListener(null);
super.onDestroyView();
}
Also as you might know, anonymous classes indirectly store reference to the parent class, so just to be safe and avoid weird circular dependencies preventing your fragment of being GCed, I would suggest declaring your anonymous classes (TextWatcher and Filter) as public static classes:
public static class MyTextWatcher implements TextWatcher
{
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
//you might need to pass this adapter as a constructor parameter
customAdapter.getFilter().filter(cs);
}
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
}
public static class MyFilter extends Filter
{
#Override
protected FilterResults performFiltering(CharSequence constraint) {
//you implementation
}
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
//you implementation
}
}
And then just instantiate those instances:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
.......
editText.addTextChangedListener(new MyTextWatcher());
.......
}
and
#Override
public Filter getFilter() {
if(filter == null)
{
filter = new MyFilter();
}
return filter;
}
Also, I've seen some memory leak issues related to EditText views. You might consider checking this post
Related
I am trying to create an edit text with a text watcher that displays the phone number correctly, to start out I am just testing it with a US number.
If I go to this site
http://libphonenumber.appspot.com/
And type in US and the number 9188143287 the results look perfect however when I implement the library as they say I am getting different results
Here is my fragment
ublic class PhoneNumberFragment extends Fragment{
private TextView next;
private EditText phoneNumber;
private PhoneNumberUtil phoneUtil;
private AsYouTypeFormatter formatter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_phone_number, container, false);
phoneUtil = PhoneNumberUtil.getInstance();
formatter = phoneUtil.getAsYouTypeFormatter("US");
init(v);
return v;
}
private void init(View v){
next = (TextView) v.findViewById(R.id.next);
phoneNumber = (EditText) v.findViewById(R.id.phoneNumber);
phoneNumber.addTextChangedListener(new PhoneNumberTextWatcher());
}
private void turnOnNext(){
next.setBackgroundResource(R.color.warningRed);
}
private class PhoneNumberTextWatcher implements TextWatcher{
private boolean isFormatting = false;
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO: implement your code
}
#Override
public void afterTextChanged(Editable s) {
// TODO: implement your code
}
#Override
public synchronized void onTextChanged(CharSequence s, int start, int before,
int count) {
if(before > count) {
return;
}
if(!isFormatting) {
isFormatting = true;
try {
formatter.clear();
for(int i=0; i<s.length();i++) {
phoneNumber.setText(formatter.inputDigit(s.charAt(i)));
phoneNumber.setSelection(phoneNumber.getText().length());
}
} catch (Exception e) {}
isFormatting = false;
}
}
}
}
My results are correct up to 7 digits, but then on the 8th digit the brackets are supposed to appear around the first 3 and they do not.
Thanks for any help!
Currently I use a RecyclerView to represent a dynamically configuration list form.
Every configuration item (entry at RecyclerView list) contains one EditText item.
To avoid wrong user input (some fields allow only integer, others only one digit after comma), I've implemented two different TextWatcher-filters which correct illegal input ("DecimalFilterDigitsAfterComma" and "DecimalFilterInteger").
My RecyclerView has 16 configuration items in total, but can only display maximum 8 at one time.
My problem is that the TextWatchers are assigned to specific Items (Integers and Decimal-Point TextEdit). But when I'm scrolling a bit, they change their order, so that Decimal- and Integer-Filters get swapped.
The TextWatcher items will be created inside the ConfigurationAdapter which is a RecyclerView.Adapter. I've event managed that the TextWatcher is only created once for each entry by using the mListConfigInit which is a boolean flag list for the items.
ConfigurationAdapter.java:
public class ConfigurationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/*
...
*/
private List<ConfigItem> mConfiguration = new ArrayList<>();
// make sure that DecimalFilter is only created once for each item
private List<Boolean> mListConfigInit = new ArrayList<>();
public ConfigurationAdapter() {
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.listitem_configuration,
parent,
false);
final ConfigurationViewHolder vh = new ConfigurationViewHolder(v);
/*
...
*/
return vh;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final ConfigurationViewHolder vh = (ConfigurationViewHolder) holder;
ConfigItem config = mConfiguration.get(position);
if(config.ShowValueAsFloat()) {
vh.SetTextWatcherType(ConfigurationViewHolder.TextWatcherType.type_FloatActive);
} else {
vh.SetTextWatcherType(ConfigurationViewHolder.TextWatcherType.type_IntActive);
}
// set name and unit
vh.mName.setText(config.mName);
vh.mUnit.setText(config.mUnit);
/*
...
*/
}
#Override
public int getItemCount() {
return mConfiguration.size();
}
public void addConfigItem(ConfigItem item) {
mConfiguration.add(item);
mListConfigInit.add(new Boolean(false));
notifyItemInserted(mConfiguration.size() - 1);
//notifyDataSetChanged();
}
/*
...
*/
}
ConfigurationViewHolder.java (changed according to pskink-comments):
public final class ConfigurationViewHolder extends RecyclerView.ViewHolder implements TextWatcher {
public TextView mName;
public CheckBox mCheckbox;
public SeekBar mSeekbar;
public EditText mValueEditText;
public TextView mUnit;
private List<TextWatcher> mListTextWatchers = new ArrayList<>();
public enum TextWatcherType {
type_FloatActive(0),
type_IntActive(1);
private int mValue;
TextWatcherType(int value) {
mValue = value;
}
int val() { return mValue; }
}
private TextWatcherType mTextWatcherType = TextWatcherType.type_FloatActive;
public ConfigurationViewHolder(View itemView) {
super(itemView);
mName = (TextView) itemView.findViewById(R.id.textView_configuration_name);
mValueEditText = (EditText) itemView.findViewById(R.id.editText_configuration_value);
mUnit = (TextView) itemView.findViewById(R.id.textView_configuration_unit);
mCheckbox = (CheckBox) itemView.findViewById(R.id.checkbox_configuration);
mSeekbar = (SeekBar) itemView.findViewById(R.id.seekBar_configuration);
mListTextWatchers.add(0, new DecimalFilterDigitsAfterComma(mValueEditText, 1));
mListTextWatchers.add(1, new DecimalFilterInteger(mValueEditText));
mValueEditText.addTextChangedListener(this);
}
public void SetTextWatcherType(TextWatcherType textWatcherType) {
mTextWatcherType = textWatcherType;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override
public void afterTextChanged(Editable editable) {
mListTextWatchers.get(mTextWatcherType.val()).afterTextChanged(editable);
}
}
DecimalFilterInteger.java
public class DecimalFilterInteger implements TextWatcher {
private final static String TAG = ConfigurationAdapter.class.getSimpleName();
private final EditText mEditText;
private String mLastTextValue = new String("");
public DecimalFilterInteger(EditText editText) {
this.mEditText = editText;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public synchronized void afterTextChanged(final Editable text) {
String strInput = text.toString().trim();
if(strInput.isEmpty()) {
return;
}
if(strInput.equals(mLastTextValue)) { // return when same value as last time to avoid endless loop
return;
}
if ((strInput.charAt(0) == '.')) { // handle dot at beginning
strInput = "";
}
if(strInput.contains(".")){ // cut trailing comma
String numberBeforeDecimal = strInput.split("\\.")[0];
strInput = numberBeforeDecimal;
}
mEditText.removeTextChangedListener(this);
mEditText.getText().clear(); // do not use setText here to avoid changing the keyboard
mEditText.append(strInput); // back to default (e. g. from 123-mode to abc-mode),
// see: http://stackoverflow.com/questions/26365808/edittext-settext-changes-the-keyboard-type-to-default-from-123-to-abc
mLastTextValue = mEditText.getText().toString();
mEditText.setSelection(mEditText.getText().toString().trim().length());
mEditText.addTextChangedListener(this);
}
}
Many thanks in advance for your help!
The cause of the swap/switching behaviour of the two different TextWatcher-implementations inside the RecyclerView was that I called removeTextChangedListenerand addTextChangedListenerinside their afterTextChanged-methods to avoid retriggering of the afterTextChanged-method.
The best way to avoid retriggering is a simple check if the text changed since the last call:
public class DecimalFilterInteger implements TextWatcher {
private final static String TAG = ConfigurationAdapter.class.getSimpleName();
private final EditText mEditText;
private String mLastTextValue = new String("");
// ...
#Override
public synchronized void afterTextChanged(final Editable text) {
String strInput = text.toString().trim();
if(strInput.isEmpty()) {
return;
}
if(strInput.equals(mLastTextValue)) { // return when same value as last time to avoid endless loop
return;
}
if ((strInput.charAt(0) == '.')) { // handle dot at beginning
strInput = "";
}
if(strInput.contains(".")){ // cut trailing comma
String numberBeforeDecimal = strInput.split("\\.")[0];
strInput = numberBeforeDecimal;
}
//mEditText.removeTextChangedListener(this); // CAUSE OF SWAP-ERROR !!!
mEditText.getText().clear(); // do not use setText here to avoid changing the keyboard
mEditText.append(strInput); // back to default (e. g. from 123-mode to abc-mode),
// see: http://stackoverflow.com/questions/26365808/edittext-settext-changes-the-keyboard-type-to-default-from-123-to-abc
mLastTextValue = mEditText.getText().toString();
mEditText.setSelection(mEditText.getText().toString().trim().length());
//mEditText.addTextChangedListener(this); // CAUSE OF SWAP-ERROR !!!
}
}
So, I have an activity with a TextView and a ListView with a custom BaseAdapter. This activity looks like this:
As you can see, every item of the list is a custom layout and the basic idea is: every time the numeric EditText within it changes, the "total" TextView from the activity (which is the sum of the prices of every product) must be updated as well.
I suppose it must somehow be done from the Adapter class, but I don't know how to do it.
My Activity file looks like this (it gets products data from server via "GetCollectionProducts" AsyncTask, where I set the adapter):
public class ProductAisleActivity extends AppCompatActivity implements View.OnClickListener{
ListView productList;
Button participate;
ImageButton search;
EditText searchET;
TextView productsTotal;
Product[] colProducts;
RelativeLayout collectionHeader;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_product_aisle);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
/* ...
Irrelevant code to this question
*/
productsTotal = (TextView) findViewById(R.id.products_aisle_total);
productsTotal.setText(
getResources().getString(
R.string.productsTotal,
String.valueOf(0.00)
)
);
productList = (ListView) findViewById(R.id.products_aisle_list);
new GetCollectionProducts().execute();
}
private class GetCollectionProducts extends AsyncTask<Void,Void,JSONArray>{
#Override
protected JSONArray doInBackground(Void... voids) {
/* Irrelevant code to this question */
}
#Override
protected void onPostExecute(JSONArray jsonArray) {
/* Irrelevant code to this question */
productList.setAdapter(
new CollectionProductsAdapter(
ProductAisleActivity.this,
colProducts
)
);
}
}
And my Adapter file looks as follows:
public class CollectionProductsAdapter extends BaseAdapter {
Context context;
ProductAisleActivity.Product[] data;
private static LayoutInflater inflater = null;
public CollectionProductsAdapter(Context context, ProductAisleActivity.Product[] data) {
this.context = context;
this.data = data;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return data.length;
}
#Override
public Object getItem(int i) {
return data[i];
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
View v = view;
if (v == null) {
v = inflater.inflate(R.layout.product_row_layout, null);
}
ProductAisleActivity.Product product = data[i];
/* ...
Irrelevant code to this question
*/
EditText productQuantity = (EditText) v.findViewById(R.id.productQuantity);
productQuantity.setText("0");
return v;
}
}
I'm stuck at this point, any help will be appreciated.
First you need to listen for any changes in the EditText so you can handle things dynamically without explicitly using something like a submit button. You can do this with a TextWatcher.
productQuanity.addTextChangedListener(new TextWatcher() {
private double originalCost = 0.0;
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Before we change the text, set the originalCost
// so we can know what the change is after the edit
originalCost = getCost(s.toString());
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// You don't need to utilize this method
}
#Override
public void afterTextChanged(Editable s) {
// After the change has taken place in the text,
// get the new cost and calculate the difference
double newCost = getCost(s.toString());
double changeInCost = newCost - originalCost;
}
private double getCost(String input){
String count = input.toString();
if(TextUtils.isEmpty(count))
return 0.0;
else
return (double) Integer.parseInt(count) * product.getPrice();
}
});
Now that we have the change in cost, what do we do with it? We need to notify the activity that we have a change. We can do that with an observer, which is fine, but for fun let's use an interface to implement a listener.
Modify your adapter class
public class CollectionProductsAdapter extends BaseAdapter {
public interface CostChangedListener{
void onCostChanged(double change);
}
Context context;
ProductAisleActivity.Product[] data;
private LayoutInflater inflater = null; // THIS SHOULDN'T BE STATIC
CostChangedListener listener;
public CollectionProductsAdapter(Context context, ProductAisleActivity.Product[] data, CostChangedListener listener) {
this.context = context;
this.data = data;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.listener = listener;
}
// The rest of your code
}
Now when we update the cost in our TextWatcher we can call
if(listener != null)
listener.onCostChanged(changeInCost);
Last, to make sure we utilize this correctly, we will need to pass a listener in our CollectionProductsAdapter constructor
productList.setAdapter(new CollectionProductsAdapter(
ProductAisleActivity.this, colProducts,
new CostChangeListener(){
#Override
public void onCostChanged(double change){
double currentTotal = Double.valueOf(productTotal.getText());
double newTotal = currentTotal + change;
productTotal.setText(String.valueOf(newTotal));
}));
Obviously you may need to tweak some of this to get it to match perfectly, and I haven't tested it so some things might be off a bit, but this should get you going in the right direction. If you have any issue feel free to comment and I will try to help you through it.
Notes
Do not keep a static reference like you were with your layout inflater
It is worth taking a look at the RecyclerView or at least the ViewHolder pattern with an Adapter
You want to add a textChangedListener for changing item values as user changes values in EditText.
You can use TextChangedListener here:
EditText myEditTextField = new EditText(this);
myEditTextField.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
You can perform tasks according to your need. It has 3 methods:
1.beforeTextChanged
2.onTextChanged
3.afterTextChanged
So you can get your task done by the help of "afterTextChanged". You have to simply call your method of calculating price for no. of items when user enters a particular number.And it will show you the price as you want.
Hope this help!
This sounds easy but it has been hard for me. I have an autocomplete textview that shows user location address. I also implemented a places api to get addresses if user enters a different location other than their location. Everything is working like it is supposed to but the places result is still showing even when there is already an address. To reduce cost I would like to get address results only when the user enters an address. I made a global boolean and set it true when the text is changed like so:
autocomplete.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
isTextEntered = true; //to get autocomplete get results only after user enters text
}
#Override
public void afterTextChanged(Editable s) {
}
});
Then I check if the boolean is true when I set my adapter as such:
if (isTextEntered) {
autocomplete.setAdapter(new GooglePlacesAutocompleteAdapter(this, R.layout.search_results_list_item, R.id.tvSearchResultItem));
autocomplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final String selectedPlace = (String) parent.getItemAtPosition(position);
autocomplete.setText(selectedPlace);
}
});
}
But doing this in oncreate method of my activity simply blocks the autocomplete from showing any places hint results. How can I accomplish my goal here? As always any help is much appreciated.
This is my custom adapter class:
class GooglePlacesAutocompleteAdapter extends ArrayAdapter implements Filterable {
private ArrayList resultList;
//private ArrayList<HashMap<String, Place>> results = new ArrayList<>();
public GooglePlacesAutocompleteAdapter(Context context, int list, int textViewResourceId) {
super(context, list, textViewResourceId);
}
#Override
public int getCount() {
return resultList.size();
}
#Override
public String getItem(int index) {
return resultList.get(index).toString();
}
//#Override
//public HashMap<String, Place> getItem(int index) {return results.get(index);}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
resultList = autocomplete(constraint.toString());
// Assign the data to the FilterResults
filterResults.values = resultList;
filterResults.count = resultList.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
return filter;
}
}
Here is my idea to achieve this.
complete.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// 1. init autocomplete
// 2. show autocomplete
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 1. get enter text
// 2. update the datasource of autocomplete adapter
}
#Override
public void afterTextChanged(Editable s) {
// hide the autocomplete
}
});
Hope it help
What I will suggest to you is just refactor it as a method like here
public void setSelectedPlace() {
autocomplete.setAdapter(new GooglePlacesAutocompleteAdapter(this, R.layout.search_results_list_item, R.id.tvSearchResultItem));
autocomplete.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final String selectedPlace = (String) parent.getItemAtPosition(position);
autocomplete.setText(selectedPlace);
}
});
}
And call it inside afterTextChanged method like here, and hide the autocomplete after that
#Override
public void afterTextChanged(Editable s) {
setSelectedPlace();
// hide the autocomplete
}
I have a ListView that contains an EditText to filter list result. Everything is working well during this phase.
My problem is after clicking an item in the list and then go back to the ListView, the item in the list is gone, most probably the because EditText contains an empty space after back button is pressed. Why is this so?
I've put a Log inside onTextChanged to see what character is being put when the back button is pressed. It's just blank. I'm not sure why this listener adds an empty space and filter the result after back button is pressed.
Here is the code in ListFragment:
public class CategoryFragment extends ListFragment {
...
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// filter result
etFilterCategory.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
// Log.d(TAG, "ontextchanged= " + charSequence);
va.getFilter().filter(charSequence);
}
#Override
public void afterTextChanged(Editable editable) {
}
});
}
}
The BaseAdapter implementing Filterable:
public class CategoryAdapter extends ArrayAdapter<CategoryModel> implements
Filterable {
...
#Override
public Filter getFilter() {
return mFilter;
}
private class ItemFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String filterString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
final ArrayList<CategoryModel> list = originalData;
int count = list.size();
final ArrayList<CategoryModel> nlist = new ArrayList<CategoryModel>(count);
String filterableString;
for (int i = 0; i < count; i++) {
filterableString = list.get(i).getName();
if(filterableString.toLowerCase().contains(filterString)) {
// add the whole custom row
nlist.add(list.get(i));
}
}
results.values = nlist;
results.count = nlist.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
arrCategory = (ArrayList<CategoryModel>) results.values;
notifyDataSetChanged();
}
}
}
How do I NOT make the ListView item filtered after every back button pressed?
EDIT:
After back button is pressed, the only way to make the ListView visible is to add a space into the EditText and remove it. That will show the previous list. What did I missed?
you may be re-creating your listAdapter object, this will nullify the previously created listAdapter.
Your call of textwatcher is pointing to older listadapter where in onResume/OnCreateView event your new instance of listadapter is generated.
I solved my problem by declaring listadapter object as member in my fragment and checking it in onResume method as follow
#Override
public void onResume() {
super.onResume();
if (news == null) {
if(CommonFun.isInternetOn(activity))
new NewsRetrivalTask().execute();
else
ToastMsg.showToast(activity, Msg.alertInternetMsg);
} else {
newsListAdapter = new NewsListAdapter(getActivity(), news);
lv.setAdapter(newsListAdapter);
lv.setOnItemClickListener(MAP_FragmentChatRoom.this);
lv.setVisibility(View.VISIBLE);
tvNoRecord.setVisibility(View.GONE);
}
}
and textwatcher in onCreateView method
etSearch.addTextChangedListener(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) {
newsListAdapter.getFilter().filter(s);
}
});