سؤال

I have a GridView in android which I fill it with data retrieved from a xml resource.
For example I have 15 items in the GridView which are placed in order. The overall height exceeds the Screen height so i have to scroll to see the rest of the items.
The problem is when I scroll back up, the order of the invisible rows have changed. It's a mysterious behavior as sometimes items swap rows with each other. Here is my getView method:

public class ImageAdapter extends BaseAdapter {
        public ImageAdapter(Context c, NodeList cuu) {
                cu = cuu;
        }
        public int getCount() {
                Log.d("Node Count",cu.getLength()+"");
                return cu.getLength();
        }
        public Object getItem(int position) {
                return position;
        }
        public long getItemId(int position) {
                return position;
        }
        public View getView(int position, View convertView, ViewGroup parent) {
                View myView = convertView;
                if (convertView == null) {
                    Node nd = cu.item(position);
                    Log.d("nodes","Pos: "+(position)+" Name: "+nd.getNodeName()+" Title: "+nd.getAttributes().getNamedItem("title").getTextContent());
                    int catID = Integer.parseInt(nd.getAttributes().getNamedItem("id").getTextContent());
                    LayoutInflater li = getLayoutInflater();
                    myView = li.inflate(R.layout.grid_item, null);
                   ImageView imageView = (ImageView) myView.findViewById(R.id.grid_item_image);
                   myView.setLayoutParams(new GridView.LayoutParams(70, 100));
                   id.download(nd.getAttributes().getNamedItem("icon").getTextContent(),imageView);
                   TextView textView = (TextView) myView.findViewById(R.id.grid_item_text);
                   textView.setText(nd.getAttributes().getNamedItem("title").getTextContent());
                   myView.setTag((Object) catID);
                }else{
                    //Log.d("nodes","Pos: "+(position));
                }
                return myView;
        }
        private NodeList cu = null;
    }

Update: Well, it's rather odd. After some more debugging I noticed that in the GridView, the Adapter skips the 13th position, meaning it returns 1 instead of 13 and then moves on to 14!!! (I guess the 13 is bad luck!)

هل كانت مفيدة؟

المحلول

If that is all the code you have in the getView method you're not implementing it right:

public View getView(int position, View convertView, ViewGroup parent) {
        View myView = convertView;
        if (myView == null) {           
            Node nd = cu.item(position);
            int catID = Integer.parseInt(nd.getAttributes().getNamedItem("id")
                    .getTextContent());
            LayoutInflater li = getLayoutInflater();
            myView = li.inflate(R.layout.grid_item, null);
            myView.setLayoutParams(new GridView.LayoutParams(70, 100));         
            myView.setTag((Object) catID);
        } 
        Node nd = cu.item(position);
        Log.d("nodes", "Pos: " + (position) + " Name: " + nd.getNodeName()
                + " Title: "
                + nd.getAttributes().getNamedItem("title").getTextContent());
        ImageView imageView = (ImageView) myView
                .findViewById(R.id.grid_item_image);
        id.download(nd.getAttributes().getNamedItem("icon")
                .getTextContent(), imageView);
        TextView textView = (TextView) myView
                .findViewById(R.id.grid_item_text);
        textView.setText(nd.getAttributes().getNamedItem("title")
                .getTextContent());
        return myView;
    }

I don't know if the code above works, yours is a bit strange. Anyway, I think the behavior you see its normal because all you do in your adapter is populating the first visible elements and then the adapter will be reusing the exact same elements when you scroll up and down because of the recycling. In the getView you should:

  • Check if the convertView is null:
    • If it is null it's time to inflate a new View for this GridView's element. You could also use the holder pattern to cache looking for the composing Views(instead of searching with findViewById everytime)(You use the setTag element for the inflated View but it's a piece of data from the Node data element ?!? What do you plan to do with it?!?)
    • If it isn't null you'll do nothing(or if you implement the holder pattern you would get the tag with the already searched Views)

And this is what you should do in that if/else statement

  • After the part above you'll populate the Views with data(so they hold the apropriate data for that position).

نصائح أخرى

public View getView(int position, View convertView, ViewGroup parent) {
    View view;
    if (convertView == null) {
        view= inflate your xml
    } else {
        view=convertView;
    }
    // all remaining code like 
    // view.findViewById(R.id.btn).setText("MyButton");                            
    // must be outside if-else
}

Your adapter implementation is fatally flawed.

In getView(), if convertView is null, you inflate a layout and populate its widgets. This is fine.

In getView(), if convertView is not null, you do absolutely nothing. This is exceedingly wrong.

What you should be doing in getView(), if convertView is not null, is still populating the cell's widgets. convertView represents a cell to be recycled, so you can skip the inflation and save CPU time, but you still have to update that cell's widgets to reflect whatever position you are supposed to be setting up in this specific getView() call.

I have just deleted the

if(convertView==null)

it works

If your Grid item has several views in it you can use getTag() and setTag() methods. In this case, grid items won't change their places on scrolling anymore and the performance still be fine:

public class SomeAdapter extends BaseAdapter {

    public static class ViewHolder {
        public TextView tvTitle;
        public ImageView imgPoster;
    }

    //...
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder grid;
        LayoutInflater inflator = activity.getLayoutInflater();

        if (convertView == null) {
            grid = new ViewHolder();
            convertView = inflator.inflate(R.layout.grid_item, null);
            grid.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
            grid.imgPoster = (ImageView) convertView.findViewById(R.id.img_poster);

            convertView.setTag(grid);   // <<-- H E R E
        } else {
            grid = (ViewHolder) convertView.getTag();   // <<-- H E R E
        }
        grid.tvTitle.setText(dataItem.getTitle());
        grid.imgPoster.setImage(dataItem.getImage());
        return convertView;
    }
}

I used this solution, based on another samples, and worked:

public View getView(int position, View convertView, ViewGroup parent) {
    View view = null;
    if (convertView == null) {
        view = inflate your xml and apply the View Holder pattern
    } else {
        view = convertView;
    }
    // retrieve the object by Holder pattern 
    // extra code here
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top