I have a ListView with names of people and an ActionBar with a Search widget.
What I want to do is focus the first item in the ListView that contains the search query as a substring.
For example if I have a person named "John Doe" and i search for "hn D" that row should be focused as long as it is the first one that contains "hn D" as a substring.
Note that I don't want the items that don't contain the substring removed from the list.
This is how I made the list.
activity_main.xml
<ListView android:id="#+id/list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
list_item.xml
<TextView
android:id="#+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dip"
android:textSize="16sp"
android:textStyle="bold" />
MainActivity.java
String[] names = {"John Doe","Mark Marky","Donald Duck","Derp Derpson"};
lv = (ListView) findViewById(R.id.list_view);
adapter = new ArrayAdapter<String>(this,R.layout.list_item, R.id.name, names);
lv.setAdapter(adapter);
I also have an action bar with the search widget and i implemented the onQueryTextChange() and onQueryTextSubmit() methods.
The question is how do I search through the list in those methods and how do I focus the appropriate list item?
To do textual search you can use a filter in your adapter
public class Adapter extends BaseAdapter implements Filterable {
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//do you work
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<Object> searchedList = new ArrayList<Object>();
results.count = searchedList.size();
results.values = searchedList;
return results;
}
};
return filter;
}
}
and use it like that :
private void searchAction(String query) {
youradapter.getFilter().filter(_query);
}
searchedittext.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
YourActivity.this.youradapter.getFilter().filter(cs);
}
#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
}
});
Related
I'm having some problem with the search bar.
i have an app that have a Listview in some activity, the listview have only strings inside it. I'v wanted to add a search functionality into this activity.
In the last few days i was searching over the web for some example of how to do that, and i founded a lot of guides and explanations of how to create a search bar, and none of them works in my app correctly and in most of them there is no explanation of the logic behind what is going on there.
Is there any simple guide/tutorial that can explain how to create a search bar (doesn't matter if it will be on the Action bar, or in the layout) that will reduce the already existing listview of strings into smaller list? (For example if you type the letter "a" so all the strings inside the listview that contains the letter "a" will be shown).
Thanks in advance!
Following Code will help your to filter the content of your listView.
Create SearcView using the layout like this:
<ImageView
android:id="#+id/icon_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:src="#drawable/icon_search" />
<EditText
android:id="#+id/et_userInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:background="#null"
android:hint="#string/search_friends_and_contacts"
android:singleLine="true"
android:textColor="#color/white"
android:textColorHint="#color/white"
android:textSize="14sp" />
<ImageView
android:id="#+id/icon_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:src="#drawable/icon_close_white" />
include this in your listview Layout.
Update your Adapter class:
implements your adapter class with Filterable. And Override getFilter method and inside your getFilter method write this code:
private SearchFilter mSearchFilter ;
#Override
public Filter getFilter() {
if (mSearchFilter == null) {
mSearchFilter = new SearchFilter();
}
return mSearchFilter;
}
now create inner private class in your adapter which extends Filter:
private class SearchFilter extends Filter {
//Invoked in a worker thread to filter the data according to the constraint.
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint != null && constraint.length() > 0) {
ArrayList<String> filterList = new ArrayList<FriendsModel>();
for (int i = 0; i < mOriginalList.size(); i++) {
if ((mOriginalList.get(i).toUpperCase())
.contains(constraint.toString().toUpperCase())) {
filterList.add(mOriginalList.get(i));
}
}
results.count = filterList.size();
results.values = filterList;
} else {
results.count = mOriginalList.size();
results.values = mOriginalList;
}
return results;
}
//Invoked in the UI thread to publish the filtering results in the user interface.
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
mListViewArrayList = (ArrayList<String>) results.values;
notifyDataSetChanged();
}
}
also define two ArrayListVariable in your adapter class like this:
ArrayList<String> mListViewArrayList ; <----- which will be used to render data from getView Method
ArrayList<String> mOriginalList ; <----- Which holds original list all the time.
And to call this filter from activity write following code in your Activity:
et_userInput.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
yourAdapter.getFilter().filter(arg0);
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
}
#Override
public void afterTextChanged(Editable arg0) {
}
});
I have little problem with filter data from List.i want to filter data from List. My Problem is wech i start to write text on edittext than the listview shows thw only first posision of the List.
Here is my Code.
MainClass
public class ItemType extends AppCompatActivity {
ListView item;
Context context=this;
List<String> allItemNames = new ArrayList<String>();
Item_Type_list_adapter adapter;
EditText search;
ArrayAdapter<String> adapter1;
String[] data = {"mehul joisar","amit mishra","amitabh","Aamir khan","jesica","katrina"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item_type);
search=(EditText)findViewById(R.id.Search);
item=(ListView)findViewById(R.id.item_type_listview);
allItemNames = new ArrayList<String>();
allItemNames.add("Coil");
allItemNames.add("Plate");
allItemNames.add("Sheet");
allItemNames.add("Extra");
adapter=new Item_Type_list_adapter(this,allItemNames);
item.setAdapter(adapter);
item.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
String data = (String) adapterView.getItemAtPosition(i);
Log.d("Positions", data);
}
});
search.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence cs, int i, int i1, int i2) {
adapter.getFilter().filter(search.getText().toString());
adapter.notifyDataSetChanged();
}
#Override
public void afterTextChanged(Editable editable) {
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_item_type, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
return super.onOptionsItemSelected(item);
}
}
ArrayAdapter Class
public class Item_Type_list_adapter extends ArrayAdapter<String> implements Filterable {
Activity context;
private final List<String> allNames;
public Item_Type_list_adapter(Activity context,List<String> allNames) {
super(context, R.layout.item_type_layout,allNames);
this.context=context;
this.allNames=allNames;
}
public View getView(int position,View view,ViewGroup parent) {
LayoutInflater inflater=context.getLayoutInflater();
View rowView=inflater.inflate(R.layout.item_type_layout, null, true);
TextView ItemName = (TextView) rowView.findViewById(R.id.ItemName);
ItemName.setText(allNames.get(position));
return rowView;
};
}
I Add the 4 value in List
1.Coil
2.Plate
3.sheet
4.extra
And when i start filter it only showing the Coil.But When i touch on listview its Log perfect Value But just Showing Wrong value.
Help me to solve this issue.
is there any missing in my code?
Thanks in advance
Try out this code for filtering ListView with EditText
activity_main.xml
<EditText
android:id="#+id/etText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10">
<requestFocus />
</EditText>
<ListView
android:id="#+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="#android:color/transparent"
android:isScrollContainer="false" />
a new TextView layout called item.xml for showing items
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</TextView>
onCreate() method inside MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Array list of items
List<String> itemsList = new ArrayList<String>();
itemsList.add("AAAA");
itemsList.add("AABC");
itemsList.add("ABCD");
itemsList.add("BBCD");
itemsList.add("BCDE");
itemsList.add("CCDE");
itemsList.add("CDEF");
// create an ArrayAdaptar from the String Array
final ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
R.layout.item, itemsList);
ListView listView = (ListView) findViewById(R.id.list);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
// enables filtering for the contents of the given ListView
listView.setTextFilterEnabled(true);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// When clicked, show a toast with the TextView text
Toast.makeText(getApplicationContext(),
((TextView) view).getText(), Toast.LENGTH_SHORT).show();
}
});
EditText myFilter = (EditText) findViewById(R.id.etText);
myFilter.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) {
dataAdapter.getFilter().filter(s);
}
});
}
hope it helps
I'm on an android project since September, and I've to deal with AutocompleteTextView.
I made my personnal ArrayAdapter to populate the dropdown suggestion list, it works, but act strangely.
When i select an item in the dropdown list, it replace the text in my textView by the selection (that's okay), but after this, reShow the Dropdown.
I want it to hide until the user type something else on the keyboard.
Here is a sample of my code :
private List<String> autocompleteAddress;
addressBox = (AutoCompleteTextView)findViewById(R.id.form_address_box);
autocompleteAdapterAddress = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, autocompleteAddress){
#Override
public Filter getFilter(){
Filter custom_filter = new Filter(){
#Override
protected FilterResults performFiltering(CharSequence constraint){
FilterResults filterResults = new FilterResults();
filterResults.values = autocompleteAddress;
filterResults.count = autocompleteAddress.size();
return filterResults;
}
#Override
protected void publishResults(CharSequence contraint, FilterResults results){
notifyDataSetChanged();
}
};
return custom_filter;
}
};
addressBox.setAdapter(autocompleteAdapterAddress);
addressBox.addTextChangedListener(new TextWatcher(){
#Override
public void onTextChanged(CharSequence s, int start, int before, int count){
//ac is an asynctask populating the suggestion list (autocompleteAddress)
GetGoogleAutocompletion ac = new GetGoogleAutocompletion();
ac.execute(addressBox.getText().toString());
}
#Override public void afterTextChanged(Editable s){}
#Override public void beforeTextChanged(CharSequence s, int start, int count, int after){}
});
addressBox.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
// here i just store the autocompleted text in a String
}
});
I thought the setText() method would be call in onItemClick(), and I could have used dismissDropDown(), but as i overrided onItemClick(), I'm pretty sure it's not there.
So i really don't know neither where the setText() is called, nor how to avoid the display of the dropDown menu after a selection.
Thanks in advance!
More than one person have had the problem of how to implement a Filter for a ListView that uses a SimpleCursorAdapter. I had this problem and I have found lots of answers about this in this web. I have taken pieces of code from everywhere until I finally got it work and it was simpler that it looked like.
I've seen the Google I/O 2010 - The world of ListView video on youtube and it says that you have to implement getFilter method and implements Filterable on your class adapter, something like this:
public class MyListCursorAdapter extends SimpleCursorAdapter implements Filterable{
...
#Override
public Filter getFilter() {
return new Filter(){
#Override
protected FilterResults performFiltering(CharSequence charSequence){
FilterResults results = new FilterResults();
//If there's nothing to filter on, return the original data for your list
if(charSequence == null || charSequence.length() == 0){
results.values = originalData;
results.count = originalData.getCount();
}
else{
Cursor filterResultsData = null;
filterResultsData = DB.getResults(charSequence);
results.values = filterResultsData;
results.count = filterResultsData.getCount();
}
return results;
}
#Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults){
filteredData = (Cursor) filterResults.values;
if (filterResults.count > 0) {
notifyDataSetChanged();
}else{
notifyDataSetInvalidated();
}
}
};
}
}
At least is what I tried to do. I've seen that using ArrayAdapter instead of Cursor, works pretty well. But in the case of Cursor it didn't work, at least not for me. Maybe I'm doing something wrong. So I was a little confused about what do to. I know that I have a edit text, a list view and everytime a put some text on my edit text I want my list get filtered.
Now I'm going to answer my own question since I want to share with you my solution.
This is What I have done:
Layout:
<EditText
android:id="#+id/editTextSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:hint="#string/search" >
</EditText>
<ListView
android:id="#id/android:list"
android:textFilterEnabled="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
Pay attention to android:textFilterEnabled="true" in ListView.
My ListActivity or ListFragment:
public class ClientesActivity extends ListFragment {
private MyListCursorAdapter myAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_clientes, container, false);
//call to a method that fill my list
myListGetFilled(view);
//Put a listener to edit text search
EditText etSearch = (EditText) view.findViewById(R.id.editTextSearch);
etSearch.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
myAdapter.getFilter().filter(cs);
}
#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
}
});
return view;
}
private void myListGetFilled(View view){
Cursor mCursor = DB.fillMyList(getActivity());
if(mCursor.getCount()>0){
String[] from = new String[] { "name", "description"};
int[] to = new int[] { R.id.textViewName, R.id.textViewDescription};
ListView myListView = (ListView) view.findViewById (android.R.id.list);
myAdapter = new MyListCursorAdapter(context, R.layout.activity_row_list, mCursor, from, to);
myListView .setAdapter(myAdapter);
}
}
And finally my custom cursor adapter:
public class MyListCursorAdapter extends SimpleCursorAdapter implements Filterable{
...
/*THE METHOD THAT DOES THE MAGIC*/
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (getFilterQueryProvider() != null){
return getFilterQueryProvider().runQuery(constraint);
}
Cursor filterResultsData = DB.getResults(constraint);
return filterResultsData;
}
}
And that's it!!.
Hope this can be useful to anyone. And if you think there is a better solution than this one, please share!!.
I'm writing a dictionary app. My search screen is very simple: centered in the Activity is the app's logo, and aligned to the bottom of the screen is the search box. When the search box receives focus, the soft keyboard pops up and the search box moves right on top of it.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView android:id="#+id/search_logo"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:src="#drawable/logo_transparent"
android:layout_centerInParent="true"
android:contentDescription="#string/desc_logo"
/>
<EditText android:id="#+id/search_fld"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/textbox"
android:inputType="text"
android:hint="#string/search_hint"
android:padding="10dp"
android:layout_alignParentBottom="true"
/>
</RelativeLayout>
As soon as the user types even a single letter, I will make a query in Lucene for matching entries. I want the view on top of the search box to be a dynamically updating ListView for every letter that is typed (or deleted), but how can I do that from this XML layout? What is the right approach to this kind of design?
Following are the pointers which will eventually guide you to your answer.
Add a textwatcher to the editfield in which you are going to write the search words..
txtSearch.addTextChangedListener(textWatcher);
In afterTextChanged method of textwatcher you will need a filter with the characters typed in searchfield as parameter, to filter out the search result.
private TextWatcher textWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
adapter.getFilter().filter(s);
adapter.notifyDataSetChanged();
}
};
Following is a class I used for filtering purpose.
/*
* Class that implements filtering functionality.
*/
public class MyFilter extends Filter {
public MyFilter(ArrayList<CustomerListRow> data) {
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
constraint = constraint.toString().toLowerCase();
FilterResults result = new FilterResults();
if (constraint != null && constraint.toString().length() > 0) {
ArrayList<CustomerListRow> filt = new ArrayList<CustomerListRow>();
for (int i = 0; i < arrayListCopy.size(); i++) {
CustomerListRow each = arrayListCopy.get(i);
if (each.getName().toLowerCase().contains(constraint)) {
filt.add(each);
}
}
result.count = filt.size();
result.values = filt;
} else {
synchronized (this) {
result.count = arrayListCopy.size();
result.values = arrayListCopy;
}
}
return result;
}
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
ArrayList<CustomerListRow> filtered = (ArrayList<CustomerListRow>) results.values;
clear();
int size = filtered.size();
for (int i = 0; i < size; i++) {
add(filtered.get(i));
}
notifyDataSetInvalidated();
}
}
You will need to create an adapter to which you will pass your complete list, which will eventually passed to the filter.
Following is constructor of my adapter class.
public MyAdapter(Context context, int textViewResourceId,
List<CustomerListRow> objects) {
super(context, textViewResourceId, objects);
this.context = context;
inflator = (LayoutInflater) context
.getSystemService(LAYOUT_INFLATER_SERVICE);
list = (ArrayList<CustomerListRow>) objects;
filter = new MyFilter(list);
arrayListCopy.addAll(list);
}
The solution seems to be more straightforward. First, create a TextWatcher for the EditText. Then, inside onTextChanged(), this is what you do:
Create a Lucene query whenever the method is invoked. Get the results.
Call clear() in the adapter.
Add all the results to the adapter.
Call notifyDataSetChanged() on the adapter.