I have some troubles: I've got a simple GridView
, in which I have two categories of items. I wanted to add three Menu Buttons
to have a Filter
for this GridView and show/hide the items with the category selected.
For the example, with the following image, the first image represents my simple GridView
with the Button in ActionBar
. When I press the Button, a SubMenu
displays three rows which are, for this example: "All", "Open" & "Closed". And when I press the "Open" button, I want to show only the items with category "Open":
I found several things for Search Editext
with the implements Filterable
but I don't think it's a good way to achieve what I want. I don't need a TextView/EditText
as filter, just a Button in ActionBar
.
Note: I updated my Adapter
after modifications with @ana01's answer.
It seems that getView()
are called once too much. I added 3 Integers
to count the number of items with their categories (nValues = total, nOpen = nb of Open item, nClosed = nb of Closed item). I used notifyDataSetChanged() to update my adapter.
Here's my Activity
with my BaseAdapter
:
public class MainActivity extends SherlockActivity {
ActionBar actionbar;
static GridView gridview;
static MyAdapter adapter;
String[] values = new String[] {
"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"
};
// 1 for Open items, 2 for Closed items
int[] vStatus = new int[] {
1, 2, 1, 2, 1
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gridview = (GridView) findViewById(R.id.grid);
adapter = new MyAdapter(this);
gridview.setAdapter(adapter);
gridview.setOnItemClickListener(new OnItemClickListener() {
// new Intent to another Activity
// ...
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.main, menu);
return true;
}
// call an adapter method filterView()
// with the integer sort by category
// 0 = All items | 1 = Open | 2 = Closed
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
return false;
case R.id.action_listfilter_all :
adapter.filterView(0);
return true;
case R.id.action_listfilter_open :
adapter.filterView(1);
return true;
case R.id.action_listfilter_closed :
adapter.filterView(2);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public class MyAdapter extends BaseAdapter {
private Context mContext;
// Initialize the category's integer "visibleFlag"
private int visibleFlag = 0;
// Initialize the counters' categories
int nValues, nOpen, nClosed;
public MyAdapter(Context c) {
mContext = c;
}
// filterView method called by option selected item menu
public void filterView(int i) {
visibleFlag = i;
// refresh the content
notifyDataSetChanged();
/* gridview.invalidateViews(); */
}
protected class ViewHolder {
TextView text, view, like, user, coms;
ImageView imageview, imageflag;
}
// return the number of items regarding by category selected
public int getCount() {
switch(visibleFlag) {
case 0: nValues = values.length; break;
case 1: nValues = nOpen; break;
case 2: nValues = nClosed; break;
}
return nValues;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
LayoutInflater inflater = ((MainActivity) mContext).getLayoutInflater();
convertView = inflater.inflate(R.layout.item_main, parent, false);
viewHolder = new ViewHolder();
viewHolder.text = (TextView) convertView.findViewById(R.id.text);
viewHolder.imageview = (ImageView) convertView.findViewById(R.id.image);
// set the tag of the category and
// augment the selected category (by + 1)
switch(vStatus[position]) {
case 1: viewHolder.imageview.setTag(1); nOpen++; break;
case 2: viewHolder.imageview.setTag(2); nClosed++; break;
}
// set the tag of the item's position
viewHolder.text.setTag(position);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
// display the text with the position's tag
viewHolder.text.setText(values[(Integer) viewHolder.text.getTag()]);
// display the image with the position's tag
switch((Integer) viewHolder.imageview.getTag()) {
case 1: viewHolder.imageview.setImageResource(R.drawable.ic_open); break;
case 2: viewHolder.imageview.setImageResource(R.drawable.ic_closed); break;
}
return convertView;
}
}
}
But this does not display the right items with the selected category!
Can somebody help me to figure it out?
You probably need the first two lines only, because gridView.setAdapter(adapter)
doesn't make sense if the adapter is already set.
I don't think you need an AsynkTask for refreshing your collection, just modify your adapter to be aware of item types: when the filter is set (an integer that represent one of the 3 states: all, closed, opened), your adapter should report the correct count and return the proper item (you could use 3 different item collections, although this means duplicating your data).
gridView.notifyDataChanged()
triggers a new getCount()
call in your adapter, followed by multiple calls to getView(..)
. The gridView.invalidateViews()
says that child views need to be redrawn. You don't need to recreate the gridView.
If your items should look similar to what you drawn, I think you could use a ListView (with the same adapter).
Update:
It's OK if getCount()
is getting called multiple times, but you should avoid performing heavy tasks in this method (not the case at the moment).
In getView(..)
you should deliver a view which is fully synchronized with the corresponding position, no matter if you reuse a previously created one or not. So the problem is that you are updating the view only if it's freshly created (convertView==null)
, although you should update the title's text &CO. right before the view is returned.
Related to your "ghost item", I don't know how big are your layout items, if they are visible all at once etc., but your adapter should create at least as many views as the visible ones.
Update2:
Modify your adapter as below, call prepareData()
in your adapter's constructor, and see if it's working:
private ArrayList<Integer> closedIndexToRealIndex;
private ArrayList<Integer> openedIndexToRealIndex;
private void prepareData(){
nOpen = nClosed = 0;
closedIndexToRealIndex = new ArrayList<Integer>();
openedIndexToRealIndex = new ArrayList<Integer>();
for(int i = 0; i < values.count; i++){
if(vStatus[i] == 0){
openedInexToRealIndex.add(Integer.valueOf(i));
nOpen++;
}
else{
closedIndexToRealIndex.add(Integer.valueOf(i));
nClosed++;
}
}
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
LayoutInflater inflater = ((MainActivity) mContext).getLayoutInflater();
convertView = inflater.inflate(R.layout.item_main, parent, false);
viewHolder = new ViewHolder();
viewHolder.text = (TextView) convertView.findViewById(R.id.text);
viewHolder.imageview = (ImageView) convertView.findViewById(R.id.image);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
int actualIndex = 0;
switch(visibleFlag){
case 0://all
actualIndex = position;
break;
case 1://opened
actualIndex = openedIndexToRealIndex.get(position).intValue();
break;
case 2://closed
actualIndex = closedIndexToRealIndex.get(position).intValue();
break;
default:
break;
}
viewHolder.text.setText(values[acutalIndex]);
if(vStatus[position] == 1)
viewHolder.imageview.setImageResource(R.drawable.ic_open);
else
viewHolder.imageview.setImageResource(R.drawable.ic_closed);
return convertView;
}