I have an ArrayAdapter (myAdapter) attached to an AutoCompleteTextView (textView) component.
Once the user presses a character I would like to populate AutoCompleteTextView's drop down list with items containing this character.
I retrieve the items using AsyncTask (which uses a web service).
I call myAdapter.add(item) but the drop down list is empty.
I added a call myAdapter.getCount() after each addition and it shows zero every time.
Calling notifyDataSetChanged() didn't help.
I even tried to add simple String objects instead of my custom objects, to no avail.
What am I doing wrong?
Edit: I changed the code as miette suggested below but still to no avail.
Generally, what I do is after text is changed in my auto complete text view, I call a new AsyncTask and pass it the entered text and a Handler (see afterTextChanged()). The task retrieves objects relevant to the text and once done the Handler's handleMessage() is called. In handleMessage() I attempt to populate the adapter's objects. But still the adapter's drop down list ends up empty.
Here is my code:
public class AddStockView extends Activity
implements OnClickListener, OnItemClickListener, TextWatcher {
ArrayAdapter<Stock> adapter;
AutoCompleteTextView textView;
Vector<Stock> stocks;
public AddStockView() {
// TODO Auto-generated constructor stub
stocks = new Vector<Stock>();
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.add_stock_view);
findViewById(R.id.abort_button).setOnClickListener(this);
adapter = new ArrayAdapter<Stock>(this,
android.R.layout.simple_dropdown_item_1line, stocks);
//adapter.setNotifyOnChange(true);
textView = (AutoCompleteTextView)
findViewById(R.id.search_edit_text);
textView.setAdapter(adapter);
textView.setOnItemClickListener(this);
textView.addTextChangedListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId())
{
case R.id.abort_button:
finish();
break;
case R.id.search_edit_text:
break;
}
}
#Override
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// TODO Auto-generated method stub
Stock stockToAdd = (Stock)parent.getAdapter().getItem(position);
//TODO: Add the above stock to user's stocks and close this screen
finish();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.layout.menu, menu);
CategoryMenu.getInstance().populateMenu(menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
CategoryMenu.getInstance().menuItemSelected(item, this);
return false;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
return true;
}
#Override
public void afterTextChanged(Editable text) {
// TODO Auto-generated method stub
if (text.toString().equals(""))
return;
new AppTask().execute(new AppTask.Payload(Consts.taskType.SEARCH_STOCK,
new Object[] {text, handler}, this));
}
#Override
public void beforeTextChanged(CharSequence a0, int a1, int a2, int a3) {
// TODO Auto-generated method stub
}
#Override
public void onTextChanged(CharSequence a0, int a1, int a2, int a3) {
// TODO Auto-generated method stub
}
private void addStockItemsToAdapter(Vector<Object> dataItems)
{
for (int i = 0; i <dataItems.size(); i++)
{
Stock stk = (Stock)dataItems.elementAt(i);
stocks.add(stk);
}
}
public void populateAdapter()
{
addStockItemsToAdapter(ContentReader.getInstance.getDataItems());
adapter.notifyDataSetChanged();
int size = adapter.getCount(); // size == 0 STILL!!!!
textView.showDropDown();
}
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
populateAdapter();
}
};
}
Thanks a lot, Rob
I had the exact same problem. After examining the ArrayAdapter and AutoCompleteTextView source code, I found out that the problem was, in short, that:
the original object list is stored in ArrayAdapter.mObjects.
However, AutoCompleteTextView enables ArrayAdapter's filtering, meaning that new objects are added to ArrayAdapter.mOriginalValues, while mObjects contains the filtered objects.
ArrayAdapter.getCount() always returns the size of mObjects.
My solution was to override ArrayAdapter.getFilter() to return a non-filtering filter. This way mOriginalValues is null and mObjects is used instead in all cases.
Sample code:
public class MyAdapter extends ArrayAdapter<String> {
NoFilter noFilter;
/*
...
*/
/**
* Override ArrayAdapter.getFilter() to return our own filtering.
*/
public Filter getFilter() {
if (noFilter == null) {
noFilter = new NoFilter();
}
return noFilter;
}
/**
* Class which does not perform any filtering.
* Filtering is already done by the web service when asking for the list,
* so there is no need to do any more as well.
* This way, ArrayAdapter.mOriginalValues is not used when calling e.g.
* ArrayAdapter.add(), but instead ArrayAdapter.mObjects is updated directly
* and methods like getCount() return the expected result.
*/
private class NoFilter extends Filter {
protected FilterResults performFiltering(CharSequence prefix) {
return new FilterResults();
}
protected void publishResults(CharSequence constraint,
FilterResults results) {
// Do nothing
}
}
}
Create an array adapter with a vector or array like:
ArrayAdapter(Context context, int textViewResourceId, T[] objects)
By initializing your arrayadapter, you will make it listen to objects array. Do not add item to the adapter or clear the adapter, do your additions in "objects" array and also clear it. After changes on this array call
adapter.notifyDataSetChanged();
More specifically
ArrayAdapter<YourContentType> yourAdapter = new ArrayAdapter<YourContentType> (this,R.id.OneOfYourTextViews,YourDataList);
yourAdapter.notifyDataSetChanged();
aTextView.setText(yourAdapter.isEmpty() ? "List is empty" : "I have too many objects:)");
This should be done after loading YourDataList, I checked your code, are you sure handler calls addStockItemsToAdapter() before you look your adapter is empty or not?
You should also check if stocks vector has any elements in it.
Where do you call addItemsToAdapter()?
Can you show us, how you have tried to add simple Strings to your Adapter?
Edit: out of the comments the helpful code sample:
adapter = new ArrayAdapter<Stock>(this, android.R.layout.simple_dropdown_item_1line, stocks);
adapter.notifyDataSetChanged();
textView.setAdapter(adapter);
Related
I am trying to write a login in Android. The Logic is I am initiating an ArrayList in a constructor of a PopupWindow. In that PopupWindow I am showing a list using RecyclerView, by passing this ArrayList into the constructor of the Adapter Class. In that list I am using an EditText to search the list using addTextChangedListener.
The code is as follows,
MainActivity.Java
ArrayList<CompanyModel> companyList , backupCompanyList;
CompanyAdapter companyAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
//initialisation of both arraylists
companyList = getCompanylist();
backupCompanyList = companyList;
}
// inner class declared in the MainActivity.java
public class ShowCompanyData{
public ShowCompanyData(){
//initialise popupwindow
//get view of recyclerview and other view components of the popupwindow , and setadapter to the recyclerview
companyAdapter = new CompanyAdapter(context , companyList );
et_search.addTextChangedListener(new TextWatcher() {
#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) {
String text = et_search.getText().toString().toLowerCase(Locale.getDefault());
companyAdapter.filter(text);
}
});
}
}
//this button belongs to the Layout file of MainActivity.
public void showPopupList(View v){
// this is a button click where i am showing the company list popupwindow
companyListPopupWindow.showAtLocation(layout, Gravity.CENTER, 0, 0);
}
CompanyAdapter.java
public class CompanyAdapter extends RecyclerView.Adapter<CompanyAdapter.ViewHolder> {
Context context;
ArrayList<CompanyModel> mainArrayList;
ArrayList<CompanyModel> list;
// other imp methods are also written , but not shown because not necessary to show here
public CompanyAdapter(Context context, ArrayList<CompanyModel> mainArrayList) {
this.context = context;
this.mainArrayList = mainArrayList;
this.list = new ArrayList<>();
this.list.addAll(mainArrayList);
}
public void filter(String charText) {
charText = charText.toLowerCase(Locale.getDefault());
mainArrayList.clear();
if (charText.length() == 0) {
mainArrayList.addAll(list);
} else {
for (CompanyModel wp : list) {
if (wp.getCompanyName().toLowerCase(Locale.getDefault()).contains(charText)) {
mainArrayList.add(wp);
}
}
}
notifyDataSetChanged();
}
}
Here the issue I am facing is, when I search something in in the EditText of PopupWindow where the list of Company's is shown, the ArrayList backupCompanyList is getting modified same as the companyList ArrayList.
My question is, I am not assigning anything to the backupCompanyList, also not passing it as a parameter to the Adapter Class, still when I debug the app the backupCompanyList are showing same contents as companyList, after searching anything in the EditText.
Where the backupCompanyList should contains the data (unchanged) assigned in OnCreate and should not modify the changes, because there are no operations or assignments done to the backupCompanyList in the entire program.
Can anyone guide me to overcome this issue.
Note :
I have not written full code, I have written only necessary code
Both ArrayLists (companyList & backupCompanyList) are showing proper data before entering any text into EditText of search. And issue is occuring only after searching.
In your Activity's onCreate method, your are assigning companyList reference to backupCompanyList reference. Both companyList and backupCompanyList are referring to the same ArrayList object reference returned from getCompanyList() method. That's why, it's reflecting both lists are changing together. In actual, there's only one ArrayList object.
Instead of:
companyList = getCompanyList();
backupCompanyList = companyList;
Use
companyList = getCompanyList();
backupCompanyList = new ArrayList<>(companyList);
Using the below code I want to simply change layouts upon spinner value selection. However, when my activity loads, the spinner never loads the values to be selected.
Oddly enough when I remove the code for (and everything below it)
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
the spinner values show up again.
public class ListCarsActivity extends Activity implements OnItemClickListener, OnClickListener, OnItemSelectedListener {
public static final String TAG = "ListCarsActivity";
Spinner spinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_cars);
spinner = (Spinner) findViewById(R.id.spinner3);
ArrayAdapter adapter= ArrayAdapter.createFromResource(this,R.array.domain,android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(this);
// initialize views
initViews();
}
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
}
#Override
public void onItemSelected(AdapterView<?> arg0, View view,
int position, long row_id) {
switch(position){
case 1:
setContentView(R.layout.list_cars);
break;
case 2:
setContentView(R.layout.list_owners);
break;
}
setContentView(R.layout.list_cars);
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
The spinner you are referring to is in your first activity. After you have replaced the content, the spinner isn't there anymore. If you have another spinner in the second layout, you have to reconnect it and set the listener again. Basically you have to run your onCreate stuff after every setContentView...
As a side note, whatever you are trying to do, this is probably not the way to go. To show another full layout, better use another activity.
I use the same activity for save and edit purpose. I have a list view.
In List fragment I have
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
if(dbHelper != null){
Item item = (Item) this.getListAdapter().getItem(position);
Intent intent = new Intent(getActivity(), SaveEditActivity.class);
intent.putExtra("Update_Item", item);
startActivity(intent);
}
}
Now in SaveEditActivity I have
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(....);
Intent intent = getIntent();
if(intent.hasExtra("Update_Item")){
Item item = (Item) intent.getSerializableExtra("Update_Item");
if(item != null){
this.isEdit = true;
this.editedItem = item;
setItemData(item); // set the data in activity
}
}
}
Now when user clicks on save button I check for isEdit flag
if(isEdit == true){
updateItem();//update
}else{
saveItem(); // add
}
}
In Update method I update the item in database using editedItem Object
private void updateItem(){
if(this.editedItem != null){
dbHelper.updateItem(editedItem);
}
}
But my list view is not refreshed with new data.
I am using customAdapter which extends BaseAdapter.
In ListFragmet I load the data into adapter in onResume() method
#Override
public void onResume() {
super.onResume();
dbHelper = new DatabaseHandler(getActivity());
List<Item> existingItems = dbHelper.getItemData();
adapter = new CustomAdapter(getActivity().getApplicationContext(), existingItems);
if(adapter != null){
this.setListAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
Now when returning from activity this method should be called and adapter should be notified of the change? Isnt that right?
Can anyone help please?
Somehow you have to notify your ListView that the data set changed/updated so that it should refresh itself. In one of my app I used the Observer interface & Observable class to observe data changed or not. This helps me to solve the case.
The idea you can use is on every update/save the data set changed, so after doing so you should call a method named notifyDataSetChanged()
/* don't be confuse with this method's name, Its a custom method. I
used this name. You may use another like myAbcdMethod() :) */
private void notifyDataSetChanged() {
setChanged();
notifyObservers(new Boolean(true));
}
notifyDataSetChanged() is a private method, & you should implement this in your DbHelper. After every edit.save you can call this.
And in your activity you should use the following to refresh the list:
#Override
public void update(Observable observable, Object data) {
isDataChanged = (Boolean) data;
}
#Override
protected void onRestart() {
super.onRestart();
if (isDataChanged) {
adapter.clear();
adapter.refresh(screenNames.toArrayList());
adapter.notifyDataSetChanged();
isDataChanged = false;
}
}
This works in single activity app to multiple activity app.
My XML:
<AutoCompleteTextView
android:id="#+id/searchAutoCompleteTextView_feed"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:completionThreshold="2"
android:hint="#string/search" />
MY java code:
AutoCompleteTextView eT = (AutoCompleteTextView)findViewById(R.id.searchAutoCompleteTextView_feed);
eT.addTextChangedListener(this);
String[] sa = new String[]{"apple", "mango", "banana", "apple mango", "mango banana"};
ArrayAdapter<String> aAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line, sa);
eT.setAdapter(aAdapter);
This is not working atall....i mean its just working like an EditTextView. Where am i wrong??
complete code:
public class FeedListViewActivity extends ListActivity implements TextWatcher{
private AutoCompleteTextView eT;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.feed);
eT = (AutoCompleteTextView) findViewById(R.id.searchAutoCompleteTextView_feed);
eT.addTextChangedListener(this);
Thread thread = new Thread(null, loadMoreListItems);
thread.start();
}
private Runnable returnRes = new Runnable() {
public void run() {
//code for other purposes
}
};
private Runnable loadMoreListItems = new Runnable() {
public void run() {
getProductNames();
// Done! now continue on the UI thread
runOnUiThread(returnRes);
}
};
protected void getProductNames() {
String[] sa = new String[]{"apple", "mango", "banana", "apple mango", "mango banana"};
ArrayAdapter<String> aAdapter = new ArrayAdapter<String>(getApplicationContext(),
android.R.layout.simple_dropdown_item_1line, sa);
eT.setAdapter(aAdapter);
}
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
}
I just saw your other question before seeing this one. I was struggling with autocomplete for some time and I almost reverted to your new implementation of downloading all the keywords until I finally got it to work. What I did was;
//In the onCreate
//The suggestArray is just a static array with a few keywords
this.suggestAdapter = new ArrayAdapter<String>(this, this.suggestionsView, suggestArray);
//The setNotifyOnChange informs all views attached to the adapter to update themselves
//if the adapter is changed
this.suggestAdapter.setNotifyOnChange(true);
In my textwatcher's onTextChanged method, I get the suggests using an asynctask
//suggestsThread is an AsyncTask object
suggestsThread.cancel(true);
suggestsThread = new WertAgentThread();
suggestsThread.execute(s.toString());
In the AsyncTask's onPostExecute I then update the autocompletetextview
//suggestions is the result of the http request with the suggestions
this.suggestAdapter = new ArrayAdapter<String>(this, R.layout.suggestions, suggestions);
this.suggestions.setAdapter(this.suggestAdapter);
//notifydatasetchanged forces the dropdown to be shown.
this.suggestAdapter.notifyDataSetChanged();
See setNotifyOnChange and notifyDataSetChanged for more information
this is a snippet from my project. I think after you got data from services all you have to do is to:
clear your previous data.
clear the previous adapter values.
then add values to your list of data using add() or addAll() method.
notify the data changed by calling notifyDataSetChanged() on adapter.
#Override
public void onGetPatient(List<PatientSearchModel> patientSearchModelList) {
//here we got the raw data traverse it to get the filtered names data for the suggestions
stringArrayListPatients.clear();
stringArrayAdapterPatient.clear();
for (PatientSearchModel patientSearchModel:patientSearchModelList){
if (patientSearchModel.getFullName()!=null){
stringArrayListPatients.add(patientSearchModel.getFullName());
}
}
//update the array adapter for patient search
stringArrayAdapterPatient.addAll(stringArrayListPatients);
stringArrayAdapterPatient.notifyDataSetChanged();
}
but before all this make sure you have attached the adapter to the auto complete textview if don't do it as follows:
ArrayAdapter<String> stringArrayAdapterPatient= new ArrayAdapter<String>(getActivity(),android.support.v7.appcompat.R.layout.select_dialog_item_material,stringArrayListPatients);
completeTextViewPatient.setAdapter(stringArrayAdapterPatient);
Use adapter.notifyDataSetChanged() method to notify the changes in the list, If that is not working then you can show DropDown manually like autoCompleteTextView.showDropDown()
AutoCompleteTextView eT = (AutoCompleteTextView)findViewById(R.id.searchAutoCompleteTextView_feed);
// eT.addTextChangedListener(this);
String[] sa = new String[]{"apple", "mango", "banana", "apple mango", "mango banana"};
ArrayAdapter<String> aAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line, sa);
eT.setAdapter(aAdapter);
its working just comment on et.addtext line...
The only working solution after updating adapter and notifying about changes instantly show dropDown is reseting AutoCompleteTextView text again, Kotlin example:
with(autoCompleteTextView) {
text = text
// Place cursor to end
}
Java something like:
autoCompleteTextView.setText(autoCompleteTextView.getText());
// Place cursor to end
AutoCompleteTextView.Invalidate()
will do it.
If anyone is using a custom object array list, and facing this issue, check your model class and see if you have override the correct variable in toString. Overriede toString if you have not override yet.
public class MyModalClass {
public int id;
public String path;
#Override
public String toString() { //include this in your model and return what you need in your drop down
return path;
}
}
The Exception:
Unable to start activity ComponentInfo{com.scytec.datamobile.vd.gui.android/com.scytec.datamobile.vd.gui.android.SelectedList}: java.lang.NullPointerException..
I just want to show checkbox list view and on every check it display "checked", simply but i don't know why this gives me an exception.
public class SelectedList extends Activity implements IObserver{
private ListView machine_listview;
ArrayAdapter<String> adapter;
ArrayList<String> arrayListofMachines;
ArrayList<String> arrayListofMachineNumbers;
Vector<MDCMachineStatus> machineStatus_vector;
Handler handler;
private static int oldPosition = 0;
private Boolean firstClick = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.machinelistview);
machine_listview = (ListView) findViewById(R.id.machine_listview);
machine_listview.setFastScrollEnabled(true);
MachineStatusSingleton.Register(this);
getData();
adapter = new ArrayAdapter<String>(SelectedList.this, R.layout.selectedlist,R.id.text1, arrayListofMachines);
machine_listview.setAdapter(adapter);
machine_listview.setSelection(oldPosition);
CheckBox chk=(CheckBox)findViewById(R.id.check);
chk.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
TextView txt=(TextView)findViewById(R.id.xtra);
if (arg1)
Log.d("", "abul, checked") ;
else
Log.d("", "abul, not checked") ;
}
}
);
machine_listview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long arg3) {
// TODO Auto-generated method stub
oldPosition = position;
MachineStatusSingleton.setMachineNumber(arrayListofMachineNumbers.get(position));
SelectedList.this.finish();
}
});
handler = new Handler(){
public void handleMessage(android.os.Message msg) {
machine_listview.setAdapter(adapter);
adapter.notifyDataSetChanged();
};
};
}
public void Update(ISubject arg0) {
// TODO Auto-generated method stub
}
#Override
public void onDestroy()
{
super.onDestroy();
MachineStatusSingleton.Unregister(this);
}
private void getData(){
machineStatus_vector = MachineStatusSingleton.GetData();
arrayListofMachines = new ArrayList<String>();
arrayListofMachineNumbers = new ArrayList<String>();
for(MDCMachineStatus temp: machineStatus_vector){
arrayListofMachines.add(temp.toString());
arrayListofMachineNumbers.add(temp.getNumber());
}
Collections.sort(arrayListofMachines);
Collections.sort(arrayListofMachineNumbers);
}
private void updateData(){
getData();
handler.sendEmptyMessage(0);
adapter.notifyDataSetChanged();
int index = machine_listview.getFirstVisiblePosition();
View v = machine_listview.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
// ...
// restore
machine_listview.setSelectionFromTop(index, top);
}
}
We run our app very well and suddenly we encounter NullPointerException or Unable to start activity etc errors.
Basically NullPointerException or Unable to start activity occurs when there is issue in onCreate() method of our Activity.
This occurs when :
We change any xml values of layout related to this Activity
If we do not map xml UI's properly in our Acivity
Try to access UI which is in another layout file.
Solution :
First Cross check all the mapped elements
Give unique naming
Directly after:
TextView txt=(TextView)findViewById(R.id.xtra);
... add this:
if (txt == null) { Log.w("", "TextView is null"); }
Assuming your null pointer exception doesn't occur until you select the checkbox, that sounds like the most likely issue. I've encountered the same when I forget that I removed the corresponding element from the XML layout, or if I got the ID wrong. Usually I wrap any actions upon an element returned by "findViewById" within a null check, to ensure that even if the retrieval fails, the app at least won't crash.
Looks like you're assigning to chk, and subsequently, txt by calling findViewById as you declare them. I had to declare them first, and then assign to them using findViewById.