Question

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":

enter image description here

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.


UPDATE:

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?

Was it helpful?

Solution

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;
        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top