【问题标题】:How can I get the position of the item within a listview when I click on a specific view?单击特定视图时,如何在列表视图中获取项目的位置?
【发布时间】:2011-03-04 17:53:45
【问题描述】:

正如标题所说,当我单击项目内部的视图时,我想知道项目的确切位置。

假设我在 ArrayAdapter 的 getView() 方法中有以下代码:

...
holder = new ViewHolder ();
holder.iconAction = (ImageView)convertView.findViewById (R.id.download_item_iconAction);
holder.iconAction.setOnClickListener (new View.OnClickListener(){
    @Override
    public void onClick (View v){
        //Item X is clicked
    }
});
...

在 onClick() 中我知道被点击的视图 v,但我不知道项目的位置。

让我们做一个技巧。当 getView() 创建视图时,我将把位置保存在 ViewHolder 中:

public View getView (int position, View convertView, ViewGroup parent){
    ViewHolder holder;
    if (convertView == null){
        holder = new ViewHolder ();
        holder.iconAction = (ImageView)convertView.findViewById (id);
        holder.iconAction.setOnClickListener (new View.OnClickListener(){
            @Override
            public void onClick (View v){
                int pos = (Integer)v.getTag ();
            }
        });
        holder.iconWait.setTag (position);

        ...
    }else{
        holder = (ViewHolder)convertView.getTag ();
    }

    ...
}

此代码有效...但并非总是如此。如果您必须滚动列表以查看所有项目,则视图将被回收。假设列表有 10 个元素,只有 5 个是可见的(可见的意思是:如果我们看到一个项目的 1 像素行,那么这个项目是可见的)。现在,如果我们向下滚动,我们将看到第六个元素,但第一个 (0) 仍然可见。我们再滚动一点,第一个元素将被隐藏,我们将看到第七个元素出现,但是这个新元素的视图是第一个元素的视图 (0)。
所以我正在保存位置: 0, 1, 2, 3, 4 和 5。第七个元素 (6) 将保存位置 0: 错误。

另一种获取点击回调的方法是使用 ListView 的 OnItemClickListener 监听器:

listView = getListView ();
listView.setOnItemClickListener (new AdapterView.OnItemClickListener(){
    @Override
    public void onItemClick (AdapterView<?> parentView, View childView, int position, long id){
        ...
    }
});

如果我向下滚动,我会得到确切的位置,但是通过这种方式,我会在单击项目时收到回调,无论单击的子视图如何。

谢谢。

【问题讨论】:

  • 奇怪的是这个非常合理的问题被否决了!

标签: android listview click android-arrayadapter


【解决方案1】:

你就快到了,你需要做的就是在你的 if/else 子句之后设置标签。这是为了确保在视图被回收以及从新视图创建时更新标签。

例如

    if (convertView == null){
        holder = new ViewHolder ();
        ...
    }else{
        holder = (ViewHolder)convertView.getTag ();
    }
 holder.iconWait.setTag (position);

【讨论】:

    【解决方案2】:

    本地匿名类可以引用final局部变量。

    通过将getView() 方法更改为读取来使position 最终化:

    public View getView (final int position, View convertView, ViewGroup parent) {
    

    然后您可以从OnClickListener 中引用位置:

    holder.iconAction.setOnClickListener (new View.OnClickListener(){
        @Override
        public void onClick (View v){
            ... use position here ...
        }
    });
    

    【讨论】:

    • 无论convertView是否为null都应该这样做,所以一定要把它移到if(convertView == null)语句之外。
    • 这比标记视图更简单,但值得注意的是,在每个 getView() 调用(不仅仅是初始膨胀)上分配一个新的 OnClick 侦听器效率低下,并且可能导致不必要的 GC 调用,并且很可能稍微降低滚动性能。
    【解决方案3】:

    如果您将 holder.iconWait.setTag (position) 移到 if/then 语句之外,您的第二个答案(在元素上设置标签)会正常工作 - 也就是说,您也应该在回收的行上设置标签,而不仅仅是新的 -膨胀的。

    【讨论】:

    • 这解决了直接的问题,没有理由必须首先使用 ViewHolder/tag 机制
    • 实际上有——您接受的最终变量解决方案会在每次填充 Row(而不仅仅是创建)时分配一个新的 onclick 侦听器,这是一个坏主意,因为它会导致大量垃圾收集当您的视图滚动时。它当然更简单,但会在一定程度上降低用户体验(尤其是在 2.3 之前的非并发 GC 手机上)。仅在初始行膨胀时设置 onClick 侦听器,然后设置标签(不分配新的点击侦听器)效率更高。
    【解决方案4】:

    我还需要添加其他数据以传递给 onClick,它也适用于字符串。

    holder.iconWait.setTag ("String data here");
    

    【讨论】:

      猜你喜欢
      • 2013-12-30
      • 1970-01-01
      • 1970-01-01
      • 2016-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多