为什么项目在Android GridView中滚动时会改变顺序?

为什么项目在Android GridView中滚动时会改变顺序?,android,android-gridview,Android,Android Gridview,我在android中有一个GridView,我用从xml资源检索的数据填充它。 例如,我在GridView中有15个按顺序排列的项目。整体高度超过屏幕高度,因此我必须滚动查看其余项目。 问题是当我向上滚动时,不可见行的顺序已经改变。这是一种神秘的行为,因为有时项目之间会交换行。 下面是我的getView方法: public class ImageAdapter extends BaseAdapter { public ImageAdapter(Context c, NodeLis

我在android中有一个GridView,我用从xml资源检索的数据填充它。
例如,我在GridView中有15个按顺序排列的项目。整体高度超过屏幕高度,因此我必须滚动查看其余项目。
问题是当我向上滚动时,不可见行的顺序已经改变。这是一种神秘的行为,因为有时项目之间会交换行。 下面是我的
getView
方法:

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;
    }

更新:嗯,这很奇怪。在进一步调试之后,我注意到在GridView中,适配器跳过了第13个位置,这意味着它返回1而不是13,然后移动到14!!!(我猜13是个坏运气!)

您的适配器实现存在致命缺陷

getView()
中,如果
convertView
null
,则展开布局并填充其小部件。这很好

getView()
中,如果
convertView
不是
null
,则您将什么也不做。这是非常错误的


如果
convertView
不是
null
,则在
getView()
中应该执行的操作仍在填充单元格的小部件
convertView
表示要回收的单元格,因此您可以跳过膨胀并节省CPU时间,但您仍然必须更新该单元格的小部件,以反映您在此特定
getView()中应该设置的位置
调用。

如果这是您在
getView
方法中拥有的全部代码,那么您没有正确实现它:

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;
    }
我不知道上面的代码是否有效,你的代码有点奇怪。无论如何,我认为您看到的行为是正常的,因为您在适配器中所做的一切都是填充第一个可见元素,然后当您由于循环而上下滚动时,适配器将重用相同的元素。在
getView
中,您应该:

  • 检查
    convertView
    是否为
    null
    • 如果为
      null
      则是时候为该
      GridView
      元素充气一个新的
      视图了。您还可以使用holder模式缓存以查找组合
      视图(而不是每次都使用
      findViewById
      进行搜索)(您使用
      setTag
      元素进行膨胀视图,但它是
      节点中的一段数据
      数据元素?!您打算如何处理它?!?)
    • 如果它不是
      null
      ,您将什么也不做(或者如果您实现了holder模式,您将获得带有已搜索
      视图的标记
这就是在if/else语句中应该做的

  • 在上面的部分之后,您将用数据填充
    视图
    (因此它们保存该位置的适当数据)

我刚刚删除了

if(convertView==null)

它有效

我使用了这个解决方案,基于另一个样本,并且工作:

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
}

如果网格项中有多个视图,则可以使用
getTag()
setTag()
方法。在这种情况下,网格项在滚动时不会再改变位置,并且性能仍然良好:

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;
    }
}
公共类SomeAdapter扩展BaseAdapter{
公共静态类视图持有者{
公共文本视图tvTitle;
公共图像查看imgPoster;
}
//...
@凌驾
公共视图getView(int位置、视图转换视图、视图组父视图){
视窗栅格;
LayoutFlater充气器=活动。GetLayoutFlater();
if(convertView==null){
grid=新的ViewHolder();
convertView=充气机。充气(R.layout.grid_项,空);
grid.tvTitle=(TextView)convertView.findViewById(R.id.tv_title);
grid.imgPoster=(ImageView)convertView.findViewById(R.id.img_海报);

convertView.setTag(网格);//适配器本身不会跳过位置。发布适配器的完整代码。好的,我把整个适配器都放在那里了。谢谢,因为我对Android编程一无所知。在你的帮助下,我可以让它工作。ty!你帮了我一天,我也遇到了同样的问题,但我应用了ViewHolder,我的图像顺序仍然是正确的正在更改。@Commonware是否所有操作,包括
findViewById()
都必须在if/else之外执行?有人在这里说了,而且这些信息对我来说是新的。@sandalone:每次调用
getView()
您需要配置行。如果
/
或者
使用什么
构造(如果有的话)取决于您自己,只要在调用
getView()
时您完全配置了行。在OP的情况下,如果
convertView
不是
null
,OP就没有配置行。
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;
    }
}