How can I get the position of the item within a li

2019-05-11 11:06发布

问题:

As the title says I want to know the exact position of the item when I click on a view that is inside the item.

Suppose I have the following code within the getView() method from ArrayAdapter:

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

Within onClick() I know the view that is clicked, v, but I don't know the position of the item.

Let's do a trick. I'm going to save the position in a ViewHolder when getView() creates the view:

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

    ...
}

This code works... but not always. If you have to scroll the list to see all the items the views are recycled. Suppose that the list has 10 elements and only 5 are visible (visible means: if we see 1 pixel line of an item, then this item is visible). Now, if we scroll down we will see the sixth element but the first (0) is still visible. We scroll a little more and the first element will be hidden and we will see that the seventh element appears, BUT the view of this new element is the view of the first element (0).
So I'm saving the positions: 0, 1, 2, 3, 4 and 5. The seventh element (6) will have saved the position 0: Wrong.

Another way to get a click callback is using the ListView's OnItemClickListener listener:

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

If I scroll down I get the exact position, but with this way I receive callbacks when the item is clicked, no matter the clicked child view.

Thanks.

回答1:

You're very nearly there, all you need to do is set the tag after your if/else clause. This is to make sure the tag is updated when the view is recycled as well as when it is created from new.

e.g

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


回答2:

Local anonymous classes can reference final local variables.

Make position final by changing your getView() method to read:

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

and then you can reference position from within the OnClickListener:

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


回答3:

Your second answer (setting a tag on an element) would work fine if you moved holder.iconWait.setTag (position) outside of the if/then statement -- that is, you should set the tag on recycled rows too, not just on newly-inflated ones.



回答4:

I also needed to add other data to pass to onClick and it worked with strings too.

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