I have created a list view with multiple items in row. I have also created a search box above. I want to implement search functionality on the basis of particular fields of the list. How can I achieve this? Any help will be appreciated.
You have to use model, listview, and customadapter with filtering for this. I have created a demo for this.
Suppose you have a model named Product, and you are displaying its content in a custom listview where name and price are displayed in a textview. I mean in a custom row having two textviews, and you want to filter the list by one of the field of custom row. Here I have filtered with "name"
Screenshots:
Initial
Filtered
Source code
Model
public class Product {
public String name;
public Integer price;
public Product(String name, Integer price) {
super();
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
}
Activity with custom adapter and listview
public class MainActivity extends Activity {
private LinearLayout llContainer;
private EditText etSearch;
private ListView lvProducts;
private ArrayList<Product> mProductArrayList = new ArrayList<Product>();
private MyAdapter adapter1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initialize();
// Add Text Change Listener to EditText
etSearch.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Call back the Adapter with current character to Filter
adapter1.getFilter().filter(s.toString());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
private void initialize() {
etSearch = (EditText) findViewById(R.id.etSearch);
lvProducts = (ListView)findViewById(R.id.lvOS);
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mProductArrayList.add(new Product("a", 100));
mProductArrayList.add(new Product("b", 200));
mProductArrayList.add(new Product("c", 300));
mProductArrayList.add(new Product("d", 400));
mProductArrayList.add(new Product("e", 500));
mProductArrayList.add(new Product("f", 600));
mProductArrayList.add(new Product("g", 700));
mProductArrayList.add(new Product("h", 800));
mProductArrayList.add(new Product("i", 900));
mProductArrayList.add(new Product("j", 1000));
mProductArrayList.add(new Product("k", 1100));
mProductArrayList.add(new Product("l", 1200));
mProductArrayList.add(new Product("m", 1000));
mProductArrayList.add(new Product("n", 1300));
mProductArrayList.add(new Product("o", 1400));
mProductArrayList.add(new Product("p", 1500));
adapter1 = new MyAdapter(MainActivity.this, mProductArrayList);
lvProducts.setAdapter(adapter1);
}
// Adapter Class
public class MyAdapter extends BaseAdapter implements Filterable {
private ArrayList<Product> mOriginalValues; // Original Values
private ArrayList<Product> mDisplayedValues; // Values to be displayed
LayoutInflater inflater;
public MyAdapter(Context context, ArrayList<Product> mProductArrayList) {
this.mOriginalValues = mProductArrayList;
this.mDisplayedValues = mProductArrayList;
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return mDisplayedValues.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
private class ViewHolder {
LinearLayout llContainer;
TextView tvName,tvPrice;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.row, null);
holder.llContainer = (LinearLayout)convertView.findViewById(R.id.llContainer);
holder.tvName = (TextView) convertView.findViewById(R.id.tvName);
holder.tvPrice = (TextView) convertView.findViewById(R.id.tvPrice);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvName.setText(mDisplayedValues.get(position).name);
holder.tvPrice.setText(mDisplayedValues.get(position).price+"");
holder.llContainer.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(MainActivity.this, mDisplayedValues.get(position).name, Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint,FilterResults results) {
mDisplayedValues = (ArrayList<Product>) results.values; // has the filtered values
notifyDataSetChanged(); // notifies the data with new filtered values
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults(); // Holds the results of a filtering operation in values
ArrayList<Product> FilteredArrList = new ArrayList<Product>();
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<Product>(mDisplayedValues); // saves the original data in mOriginalValues
}
/********
*
* If constraint(CharSequence that is received) is null returns the mOriginalValues(Original) values
* else does the Filtering and returns FilteredArrList(Filtered)
*
********/
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = mOriginalValues.size();
results.values = mOriginalValues;
} else {
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mOriginalValues.size(); i++) {
String data = mOriginalValues.get(i).name;
if (data.toLowerCase().startsWith(constraint.toString())) {
FilteredArrList.add(new Product(mOriginalValues.get(i).name,mOriginalValues.get(i).price));
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/etSearch"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<ListView
android:id="@+id/lvProducts"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
></ListView>
</LinearLayout>
row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/llContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="@+id/tvName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_weight="1"
/>
<TextView
android:id="@+id/tvPrice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_weight="1"
/>
</LinearLayout>
ListView
just disappears. –
Siegel BaseAdapter
code. I am actually using an ArrayAdapter
–
Siegel onTextChanged
method. Adapter should be set only once and then it should just get filtered only as per code I have posted. –
Mitrewort Implement filterable in your customadapter class.
public class MainActivity extends AppCompatActivity {
String names[] = {"Apple","Banana","Kiwi","Oranges","Watermelon"};
String emails[] = {"This is apple","This is banana","This is kiwi","This is oranges","This is watermelon"};
int images[] = {R.drawable.apple,R.drawable.banana,R.drawable.kiwi,R.drawable.oranges,R.drawable.watermelon};
List<ItemsModel> itemsModelList = new ArrayList<>();
ListView listView;
CustomAdapter customAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listview);
for(int i = 0;i < names.length;i++){
ItemsModel itemsModel = new ItemsModel(names[i],emails[i],images[i]);
itemsModelList.add(itemsModel);
}
customAdapter = new CustomAdapter(itemsModelList,this);
listView.setAdapter(customAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search_menu,menu);
MenuItem menuItem = menu.findItem(R.id.searchView);
SearchView searchView = (SearchView) menuItem.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
Log.e("Main"," data search"+newText);
customAdapter.getFilter().filter(newText);
return true;
}
});
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
if(id == R.id.searchView){
return true;
}
return super.onOptionsItemSelected(item);
}
public class CustomAdapter extends BaseAdapter implements Filterable {
private List<ItemsModel> itemsModelsl;
private List<ItemsModel> itemsModelListFiltered;
private Context context;
public CustomAdapter(List<ItemsModel> itemsModelsl, Context context) {
this.itemsModelsl = itemsModelsl;
this.itemsModelListFiltered = itemsModelsl;
this.context = context;
}
@Override
public int getCount() {
return itemsModelListFiltered.size();
}
@Override
public Object getItem(int position) {
return itemsModelListFiltered.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = getLayoutInflater().inflate(R.layout.row_items,null);
TextView names = view.findViewById(R.id.name);
TextView emails = view.findViewById(R.id.email);
ImageView imageView = view.findViewById(R.id.images);
names.setText(itemsModelListFiltered.get(position).getName());
emails.setText(itemsModelListFiltered.get(position).getEmail());
imageView.setImageResource(itemsModelListFiltered.get(position).getImages());
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("main activity","item clicked");
startActivity(new Intent(MainActivity.this,ItemsPreviewActivity.class).putExtra("items",itemsModelListFiltered.get(position)));
}
});
return view;
}
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if(constraint == null || constraint.length() == 0){
filterResults.count = itemsModelsl.size();
filterResults.values = itemsModelsl;
}else{
List<ItemsModel> resultsModel = new ArrayList<>();
String searchStr = constraint.toString().toLowerCase();
for(ItemsModel itemsModel:itemsModelsl){
if(itemsModel.getName().contains(searchStr) || itemsModel.getEmail().contains(searchStr)){
resultsModel.add(itemsModel);
}
filterResults.count = resultsModel.size();
filterResults.values = resultsModel;
}
}
return filterResults;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
itemsModelListFiltered = (List<ItemsModel>) results.values;
notifyDataSetChanged();
}
};
return filter;
}
}
}
full tutorial can be found here: listview with search and onItemClickListner
You can set a TextWatcher for your search box and change your Cursor on onTextChanged() on TextWatcher like Codes below :
TextWatcher filterNameTextWatcher = new TextWatcher()
public void beforeTextChanged(CharSequence s, int start, int count,int after)
{
}
public void onTextChanged(CharSequence s,int start, int before,int count)
{
Cursor FilteredNameList = ZoneCardDBAdapter.instance.CursorFilteredName(s.toString());
Listadapter.changeCursor(FilteredNameList);
}
@Override
public void afterTextChanged(Editable arg0)
{
}
};
EditText filterTextName = (EditText)this.findViewById(R.id.edtZoneCardNameFilter);
filterTextCPName.addTextChangedListener(filterNameTextWatcher);
for this, you first need to add an edittext, where you will type to filter data from the list,
then enable filteration in the list,
editText = (EditText) findViewById(R.id.searchList);
adapter = new CustomListViewAdapter(this,
R.layout.list_row, rowItems);
listView.setAdapter(adapter);
listView.setTextFilterEnabled(true);
Then you need to add TextChangeListener()
for the edittext,
editText.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
}
public void beforeTextChanged(CharSequence arg0, int arg1,
int arg2, int arg3) {
}
public void afterTextChanged(Editable arg0) {
MyActivityName.this.adapter.getFilter().filter(arg0);
}
});
Use below kind of method.
your edit text box.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable theWatchedText) {
}
});
}
SearchWithMenuActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SearchWithMenuActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/expand_layout"
android:id="@+id/recycler_view"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="vertical"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
search_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/app_bar_search"
android:icon="@drawable/ic_search_black_24dp"
android:title="@string/search"
app:showAsAction="ifRoom|withText"
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
</menu>
expand_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:background="?attr/selectableItemBackground"
android:elevation="6dp">
<androidx.cardview.widget.CardView
android:id="@+id/cardview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
app:cardCornerRadius="5dp"
app:cardUseCompatPadding="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/layout_click"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/srNo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/srNo"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/part_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="8dp"
android:text="@string/enter_the_name"
android:textSize="16sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/todo"
android:padding="8dp"
android:src="@drawable/arrow_down_24" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
ExpandModel.kt
package com.materialsouk.allcodeapp.models
class ExpandModel(private var name:String,private var expanded:Boolean = false) {
fun setName(name: String) {
this.name = name
}
fun getName():String{
return name
}
fun setExpanded(expanded: Boolean) {
this.expanded = expanded
}
fun getExpanded():Boolean{
return expanded
}
}
ExpandAdapter.kt
package com.materialsouk.allcodeapp.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.materialsouk.allcodeapp.R
import java.util.ArrayList
import android.view.animation.Animation.RELATIVE_TO_SELF
import android.view.animation.RotateAnimation
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.materialsouk.allcodeapp.models.ExpandModel
class ExpandAdapter(private var expandList: ArrayList<ExpandModel>) :
RecyclerView.Adapter<ExpandAdapter.ViewHolder>() {
class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
val srNo: TextView = itemView.findViewById(R.id.srNo)
val nameTxt: TextView = itemView.findViewById(R.id.part_name)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view: View =
LayoutInflater.from(parent.context).inflate(R.layout.expand_layout, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.srNo.text = (position + 1).toString()
holder.nameTxt.text = expandList[position].getName()
}
override fun getItemCount(): Int {
return expandList.size
}
}
SearchWithMenuActivity.kt
package com.materialsouk.allcodeapp
import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.RecyclerView
import com.materialsouk.allcodeapp.adapters.ExpandAdapter
import com.materialsouk.allcodeapp.models.ExpandModel
import java.util.*
import kotlin.collections.ArrayList
class SearchWithMenuActivity : AppCompatActivity() {
private lateinit var arrayList: ArrayList<ExpandModel>
private lateinit var adapter: ExpandAdapter
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search_with_menu)
recyclerView = findViewById(R.id.recycler_view)
arrayList = ArrayList()
arrayList.add(ExpandModel("Meet", false))
arrayList.add(ExpandModel("Ankit", false))
arrayList.add(ExpandModel("Rushil", false))
arrayList.add(ExpandModel("Abhishek", false))
arrayList.add(ExpandModel("Modi", false))
arrayList.add(ExpandModel("Ghree", false))
arrayList.add(ExpandModel("Kirtan", false))
arrayList.add(ExpandModel("Ankita", false))
arrayList.add(ExpandModel("Soham", false))
arrayList.add(ExpandModel("Ganesh", false))
arrayList.add(ExpandModel("Dixit", false))
arrayList.add(ExpandModel("Ankash", false))
arrayList.add(ExpandModel("Parth", false))
arrayList.add(ExpandModel("Pranav", false))
arrayList.add(ExpandModel("Ankit Sir", false))
arrayList.add(ExpandModel("Priya Mem", false))
arrayList.add(ExpandModel("Jinal Mem", false))
arrayList.add(ExpandModel("Bhumi", false))
arrayList.add(ExpandModel("Nidhi", false))
arrayList.add(ExpandModel("Hardik", false))
arrayList.add(ExpandModel("Mayank", false))
arrayList.add(ExpandModel("Kaushik", false))
arrayList.add(ExpandModel("Rinku", false))
arrayList.add(ExpandModel("Mom", false))
adapter = ExpandAdapter(arrayList)
recyclerView.adapter = adapter
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.search_menu, menu)
val search = menu.findItem(R.id.app_bar_search)
val searchView = search.actionView as SearchView
searchView.maxWidth = android.R.attr.width
searchView.queryHint = "Search"
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
@SuppressLint("NotifyDataSetChanged")
override fun onQueryTextChange(newText: String?): Boolean {
val freeServiceModelArrayList: ArrayList<ExpandModel> = ArrayList()
for (i in arrayList) {
if (i.getName().lowercase(Locale.getDefault()).contains(
newText!!.lowercase(
Locale.getDefault()
)
)
) {
freeServiceModelArrayList.add(i)
}
}
adapter = ExpandAdapter(freeServiceModelArrayList)
recyclerView.adapter = adapter
adapter.notifyDataSetChanged()
return true
}
})
return super.onCreateOptionsMenu(menu)
}
}
© 2022 - 2024 — McMap. All rights reserved.