Android AutoCompleteTextView showing wrong suggestions - java

I had the AutoCompleteTextView working perfectly until I decided to use a custom adapter, that way I could customize the look of each row. Here is what now happens:
As you can see, the suggestion is wrong. What is happening is as I type, it shows the correct number of suggestions, but the actual names of them are the first ones in the list. So at this point it should show only one suggestion which is Texas A&M, but it instead just shows the first one in the list. Here is how I am implementing my adapter.
//setup filter
List<String> allCollegesList = new ArrayList<String>();
for(College c : MainActivity.collegeList)
{
allCollegesList.add(c.getName());
}
AutoCompleteDropdownAdapter adapter = new AutoCompleteDropdownAdapter(main, R.layout.list_row_dropdown, allCollegesList);
//the old way of using the adapter, which worked fine
//ArrayAdapter<String> adapter = new ArrayAdapter<String>(main, android.R.layout.simple_dropdown_item_1line, allCollegesList);
textView.setAdapter(adapter);
As well as my actual adapter class:
public class AutoCompleteDropdownAdapter extends ArrayAdapter<String>{
MainActivity main;
int rowLayout;
List<String> allCollegesList;
public AutoCompleteDropdownAdapter(MainActivity main, int rowLayout, List<String> allCollegesList) {
super(main, rowLayout, allCollegesList);
this.main = main;
this.rowLayout = rowLayout;
this.allCollegesList = allCollegesList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try{
if(convertView==null){
// inflate the layout
LayoutInflater inflater = ((MainActivity) main).getLayoutInflater();
convertView = inflater.inflate(rowLayout, parent, false);
}
// object item based on the position
String college = allCollegesList.get(position);
// get the TextView and then set the text (item name) and tag (item ID) values
TextView collegeTextView = (TextView) convertView.findViewById(R.id.dropDownText);
collegeTextView.setText(college);
collegeTextView.setTypeface(FontManager.light);
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
}

What happens here is that when the AutoCompleteTextView calls the getFilter method of the ArrayAdapter a Filter is returned, that takes care of filtering the ArrayAdapter, but not your allCollegesList. When you type your first characters, the methods of the Filter are called and ArrayAdapter has filtered elements at the first positions (0, 1, ...). However when the AutoCompleteTextView uses your implementation to get the Views. You use your list as if no filtering was done and use the first elements of the unfiltered list.
You can filter your own list too by overriding the getFilter method of your adapter. But that would be more coding than necessary.
You can use the ArrayAdapter's methods and not your own list, instead:
Use
String college = getItem(position);
instead of
String college = allCollegesList.get(position);
BTW:
you can get the context from parent too, using the getContext() method. That way you can decouple the Adapter from MainActivity.

Related

Search listview but instead of filtering, just inflate and display the valid items

I have a arraylist of buttons (reserveButtons) that I can display in a listview. I have made a search function which searches in my database and outputs a list of integers (resultID). They correspond to the indexes of reserveButtons I want to display.
Simply put, I want to do something like this when the search button is clicked:
ArrayAdapter<ReserveButton> adapter = new MyListAdapter();
ListView list = (ListView) myView.findViewById(R.id.resultslist);
list.setAdapter(adapter);
for (int result : resultID) {
adapter.add(reserveButtons.get(result));
}
So, for each result, I want to add the corresponding button to the listview.
Here is the private class MylistAadapter :
private class MyListAdapter extends ArrayAdapter<ReserveButton> {
public MyListAdapter() {
super(getActivity(), R.layout.list_item, reserveButtons);
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if(itemView == null) {
itemView = gettActivity().getLayoutInflater().inflate(R.layout.list_item, parent, false);
}
ReserveButton currentButton = reserveButtons.get(position);
//the resultItem is the id of the buttons
Button butt = (Button) itemView.findViewById(R.id.resultItem);
butt.setBackground(currentButton.getImage());
return itemView;
}
}
I know that getView will just display every reserveButton, but I want the code in getView to be executed when I add each button, but the position doesn't change since position = result in the for loop of the first code block.
//This code is inside MyListAdapter
#Override
public void add(ReserveButton object) {
/* What do I write here to inflate a list_item and give it
the background image reserveButton.get(result).getImage() */
super.add(object);
}
How do I override the add method of MyListAdapter so that I can add a reserveButton and change its background image for each result in the resultID list.
If the same thing can be accomplished without the add method, please do tell.
P.S: I do not want to just list every reserveButton and then filter them with the search; I want to display ONLY the buttons that the user is looking for.
I figured it out myself!
Basically, what I did was create a separate ArrayList of ReserveButtons and do the foreach loop like so:
int index = 0;
for (int result : resultID) {
//result is the single ID of an answer
answerButtons.add(index,reserveButtons.get(result));
index ++;
}
populateListView();
So I end up storing ONLY the buttons I want to display in the answerButtons list. And here is what happens in populateListView()
private void populateListView() {
ArrayAdapter<ReserveButton> adapter = new MyListAdapter();
ListView list = (ListView) myView.findViewById(R.id.resultslist);
list.setAdapter(adapter);
}
and the getView() method:
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if(itemView == null) {
itemView = getActivity().getLayoutInflater().inflate(R.layout.list_item, parent, false);
}
//Just set the image of the corresponding answerButton
ReserveButton currentButton = answerButtons.get(position);
Button butt = (Button) itemView.findViewById(R.id.resultItem);
butt.setBackground(currentButton.getImage());
return itemView;
}
Problem solved. I haven't seen any answers to a problem like this, so this post should make it easily google-able for any newcomer who stumbles upon this problem.

ListView with images only

I was just asking myself if there was a way of making a ListView without any texts. I've only found ListViews with images + texts, and that exactly what i dont want. I just want to add a lot of images like in a list and don't want to make it with ImageView because that makes my app crash. Also, i dont want my app to lag. Thanks!
this is what i got in my activity_main.xml
<ListView
android:id="#+id/listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
and i found this from another post that actually helped me, but it doesnt display all images
public class MainActivity extends Activity {
// Array of integers points to images stored in /res/drawable-hdpi/
int[] vehs = new int[]{
R.drawable.veh1,
R.drawable.veh2,
R.drawable.veh3,
R.drawable.veh4,
R.drawable.veh5,
R.drawable.veh6,
R.drawable.veh7,
R.drawable.veh8,
R.drawable.veh9,
R.drawable.veh10,
R.drawable.veh11,
R.drawable.veh12,
R.drawable.veh13,
R.drawable.veh14,
R.drawable.veh15,
R.drawable.veh16,
R.drawable.veh17,
R.drawable.veh18,
R.drawable.veh19,
R.drawable.veh20,
R.drawable.veh21,
R.drawable.veh22,
R.drawable.veh23,
R.drawable.veh24,
R.drawable.veh25,
R.drawable.veh26,
R.drawable.veh27,
R.drawable.veh28,
R.drawable.veh29,
R.drawable.veh30,
R.drawable.veh31,
R.drawable.veh32,
R.drawable.veh33,
R.drawable.veh34,
R.drawable.veh35,
R.drawable.veh36,
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Each row in the list stores country name, currency and flag
List<HashMap<String,String>> aList = new ArrayList<HashMap<String,String>>();
for(int i=0;i<10;i++){
HashMap<String, String> hm = new HashMap<String,String>();
hm.put("vehs", Integer.toString(vehs[i]) );
aList.add(hm);
}
// Keys used in Hashmap
String[] from = { "vehs","txt","cur" };
// Ids of views in listview_layout
int[] to = { R.id.vehs,R.id.txt,R.id.cur};
// Instantiating an adapter to store each items
// R.layout.listview_layout defines the layout of each item
SimpleAdapter adapter = new SimpleAdapter(getBaseContext(), aList, R.layout.listview_layout, from, to);
// Getting a reference to listview of main.xml layout file
ListView listView = ( ListView ) findViewById(R.id.listview);
// Setting the adapter to the listView
listView.setAdapter(adapter);
}
}
Didn't edit all the code, so you might find some things confusing xD
The way ListView displays data is by using an adapter. The adapter takes your data, inserts it into a custom view, and then adds it to the list.
To build a fast images ListView, the first thing you'd want to do is to add Picasso to your project. This library will automatically download and cache your images, handle ImageView recycling, and more.
The next thing you'd want to do is to write your item view. If you want an images-only list, the view could be as simple as:
<!-- res/layout/list_item.xml -->
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight" />
Next, you want to create your adapter. It accepts a List<String> of image URLs as an input, builds the items and inserts them to the ListView.
public class ImageListAdapter extends ArrayAdapter<String> {
List<String> items;
public ImageListAdapter(Context context, int resource, List<String> items) {
super(context, resource, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.list_item, null);
}
Picasso.with(getContext())
.load(items.get(position))
.into((ImageView) convertView);
return convertView;
}
}
Read the Picasso library documentation if you want to add more options such as image placeholders, transformations, and more.
Finally, to use the adapter on your ListView, add this to your activity's onCreate method:
List<String> images = new ArrayList<String>();
images.add("http://hometowncolumbia.files.wordpress.com/2007/12/lol-cats-dont-look-behind-cat.jpg");
images.add("http://i232.photobucket.com/albums/ee245/topswing/cat-lol.jpg");
listView = (ListView)findViewById(R.id.myListView);
listView.setAdapter(new ImageListAdapter(this, R.layout.list_item, images))
In a real application though, you'd probably want to load the image list from your server. You'd need to use an AsyncTask for that.
Don't make things difficult, the solution is simple in the classic 3 steps
Step 1:
Make a RelativeLayout in XML. Move the image to desired position (always to center, but you can choose if left or right) and change layout_height to wrap_content
Step 2:
Create a custom adapter for ListView, if you do this, you'll be able to all any layout as an item
public class ImagesAdapter extends ArrayAdapter<String> {
private Activity mContext;
private ArrayList<Bitmap> mImages;
public ImagesAdapter(Activity context, ArrayList<String> listOfValues, ArrayList<Bitmap> images) {
//The listOfValues is used when you make item click to get value
//Each image must to have a text value
super(context, R.layout.yourlayout, listOfValues);
mContext = context;
mImages = images;
}
#Override
public void getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = mContext.getLayoutInflater();
View returnView = inflater.inflate(R.layout.yourlayout, null);
ImageView imageView = (ImageView) returnView.findViewById(R.id.yourimageid);
imageView.setImageBitmap(mImages.get(position));
return returnView;
}
}
Step 3:
Implement in your code
ImagesAdapter adapter = new ImagesAdapter(this, myList, myImages);
mylistView.setAdapter(adapter);
myListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//volov = Values Of ListOfValues
String volov = (String) myListView.getItemPosition(position);
Log.i(TAG, "listOfValues is useful " + volov);
}
});

Example of custom setDropDownViewResource spinner item

I would like to display two values in an drop down view of my spinner.
Currently, it only has a city name, but I would also like to add a small distance field to it.
MyCity<MyCityDistance> dataAdapter;
dataAdapter = new MyCity(this, R.layout.mycityrow, list);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
I have all the code for custom data adapter, exapanding my view and holder etc.
However, the item which gets show doesn't display both the city and its distance from my current location.
It only shows what is overridden in toString() method of MyCityDistance class.
I even tried setting
dataAdapter.setDropDownViewResource(R.layout.mycityrow);
but, no success. It throws an error.
04-02 11:05:22.600: E/AndroidRuntime(367): java.lang.IllegalStateException: ArrayAdapter requires the resource ID to be a TextView
04-02 11:05:22.600: E/AndroidRuntime(367): at android.widget.ArrayAdapter.createViewFromResource(ArrayAdapter.java:347)
04-02 11:05:22.600: E/AndroidRuntime(367): at android.widget.ArrayAdapter.getDropDownView(ArrayAdapter.java:376)
04-02 11:05:22.600: E/AndroidRuntime(367): at android.widget.Spinner$DropDownAdapter.getDropDownView(Spinner.java:332)
What is a good example of creating your own custom setDropDownViewResource()?
Even if I comment out the setDropDownViewResource() line, I get the same error.
Note: The only effect mycityrow current is that the first element of Spinner is show as per the layout of mycityrow. However, when I click open the drop down, that layout is lost. I want the same layout during drop down selection too.
Note the below example uses the inbuilt android.R.layout.simple_list_item_2, Unfortunately the text color will probably be the same as the background. You can simply solve this by creating your own custom view and use it in the adapter instead.
Let me know if i should explain any part of it.
public class MainActivity extends Activity {
class City {
public City(String city, int d) {
this.city = city;
this.distance = String.valueOf(d);
}
String city;
String distance;
}
class CityAdapter extends ArrayAdapter<City> {
public CityAdapter(Context context, List<City> objects) {
super(context, android.R.layout.simple_list_item_2, objects);
}
#Override //don't override if you don't want the default spinner to be a two line view
public View getView(int position, View convertView, ViewGroup parent) {
return initView(position, convertView);
}
#Override
public View getDropDownView(int position, View convertView,
ViewGroup parent) {
return initView(position, convertView);
}
private View initView(int position, View convertView) {
if(convertView == null)
convertView = View.inflate(getContext(),
android.R.layout.simple_list_item_2,
null);
TextView tvText1 = (TextView)convertView.findViewById(android.R.id.text1);
TextView tvText2 = (TextView)convertView.findViewById(android.R.id.text2);
tvText1.setText(getItem(position).city);
tvText2.setText(getItem(position).distance);
return convertView;
}
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Spinner spinner = (Spinner)findViewById(R.id.spinner1);
List<City> list = new ArrayList<MainActivity.City>();
for(int i = 0; i < 10; i++)
list.add(new City(String.format("City %d", i + 1), (i + 1) * 1000));
spinner.setAdapter(new CityAdapter(this, list));
}
}
Try commenting the line dataAdapter.setDropDownViewResource() and the adapter will try to use the mycityow layout file for the drop down as well. Works in simple cases.

Custom ArrayAdapter not displaying results

This one has me quite stumped. I'm sure it is just something simple I am missing, but I cant seem to find out what...
When I run the program, it opens up the dialog box and displays the AutoCompleteTextView I have initialized. When I try to type something into it, nothing drops down or is displayed other than the text I type in. I have created a similar system in another part of my program with the same mechanics, but using a regular ArrayAdapter and it works fine so the interface is not the problem.
Here is where I initialize my custom ArrayList. I have been trying to use just strings to make it simpler.
final Dialog weaponDialog = new Dialog(BattleScreen.this);
weaponDialog.setContentView(R.layout.weapon_selection_dialog);
weaponDialog.setTitle("Add a Weapon");
weaponDialog.setCancelable(true);
String[] weaponStringArrayList = ConstantEquipmentHelper.getCondensedWeaponString();
WeaponArrayAdapter weaponAdapter = new WeaponArrayAdapter(this, R.layout.weapon_list_item, weaponStringArrayList);
weaponDialogAcTextView = (AutoCompleteTextView) weaponDialog.findViewById(R.id.weaponSelectionAutoCompleteTxt);
weaponDialogAddButton = (Button) weaponDialog.findViewById(R.id.weaponSelectionAddButton);
weaponDialogWeaponInfo = (TextView) weaponDialog.findViewById(R.id.weaponSelectionInformationTxt);
...
...
...
Here is my custom ArrayAdapter Class
public class WeaponArrayAdapter extends ArrayAdapter<String> {
private Context context;
String[] objects;
public WeaponArrayAdapter(Context context, int textViewResourceId, String[] objects) {
super(context, textViewResourceId);
this.objects = objects;
this.context = context;
}
private class WeaponItemHolder {
TextView weaponName;
TextView weaponCat;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//return super.getView(position, convertView, parent);
final WeaponItemHolder holder;
if (convertView == null) {
//Sets up a new holder to temporaraly hold the listeners that will be assigned to the binded variables
holder = new WeaponItemHolder();
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.weapon_list_item, null);
//Find the IDs! Find them!!!!
holder.weaponName = (TextView) convertView.findViewById(R.id.weaponListItemName);
holder.weaponCat = (TextView) convertView.findViewById(R.id.weaponListItemCategory);
//"Sets the tag associated with this view. A tag can be used
//to mark a view in its hierarchy and does not have to be unique within the hierarchy."
convertView.setTag(holder);
} else {
holder = (WeaponItemHolder) convertView.getTag();
}
String spellName = objects[position];
String[] weaponInfo = spellName.split("\\:");
weaponInfo[1] = weaponInfo[1].trim();
holder.weaponName.setText(weaponInfo[0]);
holder.weaponCat.setText(weaponInfo[1]);
return convertView;
}
}
Additional Info: I have tried debugging it and it never reaches getView. This makes sense of course, as its not displaying anything.
Thanks,
-Andrew
EDIT: I have found out how to implement the above problem:
I used a SimpleAdapter with a custom layout.
However, now I can not select any of the items... onItemClick is not even called when I try to click it. It probably has to do with using the SimpleAdapter??
LINK: http://lemonbloggywog.wordpress.com/2011/02/15/customer-autocomplete-contacts-android/
ArrayList<Map<String, String>> weaponStringArrayList = ConstantEquipmentHelper.getCondensedWeaponString();
//The adapter that recieves the layout type from android and the array creatd by the above function.
SimpleAdapter simpleAdapter = new SimpleAdapter(this, weaponStringArrayList, R.layout.weapon_list_item ,new String[] {"name", "category"}, new int[] { R.id.weaponListItemName, R.id.weaponListItemCategory});
//Find the view blah blah blah...
weaponDialogAcTextView = (AutoCompleteTextView) weaponDialog.findViewById(R.id.weaponSelectionAutoCompleteTxt);
weaponDialogAddButton = (Button) weaponDialog.findViewById(R.id.weaponSelectionAddButton);
weaponDialogWeaponInfo = (TextView) weaponDialog.findViewById(R.id.weaponSelectionInformationTxt);
//Set that adapter!
weaponDialogAcTextView.setAdapter(simpleAdapter);
You have to implement getCount() and set the count of your data, i.e. objects.length.
You also have to set the adapter to the view using the method setAdapter().
Hope this helps!

Help on a simple Android ListView problem

I am new to Android, so this may seem like a basic question. But what I got is a very simple class called SingleItem, which has an integer and a String and a getter and setter for each. In my application, I got an ArrayList which holds a collection of SingleItem objects. I also have a layout with a ListView widget.
What I am trying to do is populate the ListView with my String value in SingleItem, but when a user selects an item from the ListView, I need the integer ID from that SingleItem value. How do I do this in Android development?
If you are using your own adapter to populate the list then in the getView() function when building the view to return you can call setTag() on the view you are returning and store the entire "SingleItem" object. Then in the onClickListener of the views you return you can retrieve your info using the getTag() method of the view that has been clicked.
EDIT:
Specified which onClickListener I am referring to
here is a bunch of pseudo code: create your own adapter. This will give the flexibility to do all kinds of things but important to you here is displaying only the relevant fields from your custom class and make more complicated listviews. a decent tutorial is here: http://developerlife.com/tutorials/?p=327
You will have to handle the other overrides of baseadapter but the key is assigning the value of singleItem.getString()
public class SingleItemAdapter extends BaseAdapter{
private ArrayList<SingleItem> m_items= new ArrayList<SingleItem>();
private Context mContext;
public SingleItemAdapter (Context c,ArrayList<SingleItem> items) {
mContext = c;
m_items= items;
}
.
.
.
#Override
public Object getItem(int arg0) {
return m_items.get(arg0);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.singleitemview, null);
}
SingleItem i=(SingleItem) getITem(position)
if(v!=null){
TextView tv=(TextView) v.findViewById(R.id.yourListItemView);
tv.setText(i.getStringValue());
}
}
}
After defining your custom adapter, you can then assign it to the listview and assign a listener to the OnItemSelectedListener. since this returns the position, you can tie that back to the position in your ArrayList of SingleItems.
.
.
.
SingleItemAdapter sia=new SingleItemAdapter(this,yourArray);
yourArrayList.setAdapter(sia);
yourArrayList.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View v, int position, long row) {
SingleItem si= yourArray.getItem(position);
//do something with si.getValue();
}
.
.
.
});

Categories