Image list view filter
How to filter in a image list view, please join
Introduction
In Smart Phones, it is easy to find an application which has a filter function in a list view. The filter function will help the user to easily find his record which he wants to view. But how do we do it?
I will give a simple sample to help you clear it.
- Display a list of football players. Each player will have:
- Player thumbnail.
- Player name.
- An edit text where the user can input the character which is the beginning of the player's name. Based on the input text, the list will be filtered by the player's name and display.
- E.g.: If the user wants to view Ronaldo, he can input R, next o, ...
I hope you enjoy it :)
Using the code
Prepare to display list view data
Please refer to the code below to display list player data.
public class MainActivity extends Activity {
public ArrayList<FootballPlayer> listDatas = new ArrayList<MainActivity.FootballPlayer>();
public FootballPlayerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create dummy data
String[] userNames = new String[] { "Ronaldo", "Zidance", "Cong Vinh",
"Huynh Duc", "Gerrard", "Nagatomo", "Messi", "Minh Phuong",
"neymar" };
int[] playerDrawableResourceIds = new int[] { R.drawable.ronaldo,
R.drawable.zindance, R.drawable.congvinh, R.drawable.huynhduc,
R.drawable.gerrard, R.drawable.nagatomo, R.drawable.messi,
R.drawable.minhphuong, R.drawable.neymar };
// Prepare dummy data
for (int i = 0; i < playerDrawableResourceIds.length; i++) {
FootballPlayer footballPlayer = new FootballPlayer();
footballPlayer.name = userNames[i];
footballPlayer.imageResourceId = playerDrawableResourceIds[i];
listDatas.add(footballPlayer);
}
ListView lv = (ListView) findViewById(R.id.lv);
adapter = new FootballPlayerAdapter();
lv.setAdapter(adapter);
}
private class FootballPlayerAdapter extends BaseAdapter {
@Override
public int getCount() {
return listDatas.size();
}
@Override
public FootballPlayer getItem(int position) {
return listDatas.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
PlayerViewHolder holder;
if (convertView == null) {
convertView = (ViewGroup) LayoutInflater.from(
getApplicationContext()).inflate(
R.layout.football_player_item, null);
holder = new PlayerViewHolder();
holder.name = (TextView) convertView
.findViewById(R.id.player_name);
holder.thumb = (ImageView) convertView
.findViewById(R.id.player_thumbnail);
convertView.setTag(holder);
} else {
holder = (PlayerViewHolder) convertView.getTag();
}
holder.name.setText(getItem(position).name);
holder.thumb.setImageResource(getItem(position).imageResourceId);
return convertView;
}
}
private static class PlayerViewHolder {
public ImageView thumb;
public TextView name;
}
public class FootballPlayer {
/** Keep the id of resource file which is player's thumbnail. */
public int imageResourceId;
/** Player name. */
public String name;
}
}
The layout of each player item football_player_item.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="35dp"
android:orientation="horizontal" >
<ImageView
android:id="@+id/player_thumbnail"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="5dp" />
<TextView
android:id="@+id/player_name"
android:textColor="@android:color/black"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:orientation="vertical"
android:singleLine="true"
android:textSize="16sp" />
</LinearLayout>
The code above has some dummy data, and in fact you must create it and save to a drawable resource folder. With me, there is no way easier than downloading from the web :D
int[] playerDrawableResourceIds = new int[] { R.drawable.ronaldo,
R.drawable.zindance, R.drawable.congvinh, R.drawable.huynhduc,
R.drawable.gerrard, R.drawable.nagatomo, R.drawable.messi,
R.drawable.minhphuong, R.drawable.neymar };
And this is the result:
Add edit text to filter list data by player's name
- When input text to edit text, it will auto filter the current list data.
- If edit text is empty, display full list
To filter list, beside ArrayList<FootballPlayer> listDatas
which is used as adapter data, create a backup data list
ArrayList<FootballPlayer> listBackupDatas
.
// Prepare dummy data
for (int i = 0; i < playerDrawableResourceIds.length; i++) {
FootballPlayer footballPlayer = new FootballPlayer();
footballPlayer.name = userNames[i];
footballPlayer.imageResourceId = playerDrawableResourceIds[i];
listDatas.add(footballPlayer);
listBackupDatas.add(footballPlayer);
}
Add to the main layout an edit text where the input filter text will be.
EditText filterText = (EditText) findViewById(R.id.filter_text);
filterText .addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int arg1, int arg2,
int arg3) {
adapter.getFilter().filter(s.toString());
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1,
int arg2, int arg3) {
}
@Override
public void afterTextChanged(Editable arg0) {
}
});
The above code means that, we will control when the filter
edit text is changed. If there is changing, we call the filter
function in the player adapter
.
adapter.getFilter().filter(s.toString());
The PlayerFilter
class will be implemented as here:
private class PlayerFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
// We implement here the filter logic
ArrayList<FootballPlayer> filters = new ArrayList<FootballPlayer>();
if (constraint == null || constraint.length() == 0) {
// No filter implemented we return all the list
for (FootballPlayer player : listBackupDatas) {
filters.add(player);
}
results.values = filters;
results.count = filters.size();
} else {
// We perform filtering operation
for (FootballPlayer row : listBackupDatas) {
if (((FootballPlayer) row).name.toUpperCase().startsWith(
constraint.toString().toUpperCase())) {
filters.add(row);
}
}
results.values = filters;
results.count = filters.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
if (results.count == 0) {
listDatas.clear();
adapter.notifyDataSetChanged();
} else {
listDatas.clear();
ArrayList<FootballPlayer> resultList = (ArrayList<FootballPlayer>) results.values;
for (FootballPlayer row : resultList) {
listDatas.add(row);
}
adapter.notifyDataSetChanged();
}
}
}
performFiltering
This method is invoked when there is a callback onTextChanged
of filterText
.
- The input text of
filterText
is the parameterconstraint
. - Based on
constraint
text, suitable data will be returned.
With this sample, it will find in
listBackupDatas
the player who has the name whose starting text is
constraint
, and add to an array list.
The return data of method is a
FilterResults
object. It contains:
-
values
: the arraylist contains players whosename
start withconstraint
text. -
count
: the number of above arraylist.
publishResults
This method is invoked to notify the filter result to the UI thread. With this sample:
- If there is no data returned from
performFiltering
the data of list viewlistDatas
will be clear andadapter.notifyDataSetChanged()
. The list will be empty in this case, of course. - Otherwise, based on the
FilterResults
which was returned fromperformFiltering
method, add all player ofFilterResults
.values
toadapter.listDatas
then calladapter.notifyDataSetChanged()
to refresh the list view.
And final result is here:
Points of interest
With my experience, it is better if for each filter list we create two list data, once for displaying and and once for backup. It will help with easy controlling. In this sample, they are:
public ArrayList<FootballPlayer> listDatas = new ArrayList<MainActivity.FootballPlayer>();
public ArrayList<FootballPlayer> listBackupDatas = new ArrayList<MainActivity.FootballPlayer>();
listDatas
will be used as the main data of the adapter. Its data will be changed while filtering.-
listBackupDatas
is used as backup data.
For more, you can use the edit text in this tip to have an edit text with search icon.:)
History
- 2013 - 08 - 11: First version.