Пользовательский адаптер: получить номер элемента, по которому щелкнули, в расширенном списке

У меня есть собственный базовый адаптер, который выполняет ленивую загрузку некоторых изображений, а затем раздувает макет, поэтому я получаю список, в котором у меня есть и изображение, и текст в одной строке.

Когда пользователь нажимает один элемент списка, скажем, например, элемент 0 (верхний элемент), я хочу показать диалоговое окно с определенным содержимым. Это содержимое зависит от номера элемента, поэтому содержимое, отображаемое для элемента 0, отличается от содержимого элемента 1 и т. д.

Вот метод getView пользовательского адаптера:

public View getView(int position, View convertView, ViewGroup parent)
{
    View vi=convertView;        
    if(convertView==null)
    {
        vi = inflater.inflate(R.layout.facebook_item, null);                        
        vi.setClickable(true);
        vi.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View v)
            {                   
                String s = "test";
                Toast.makeText(myContext, s, Toast.LENGTH_SHORT).show();
            }
        });
    }        

    TextView text=(TextView)vi.findViewById(R.id.text);
    ImageView image=(ImageView)vi.findViewById(R.id.image);
    text.setText(msg[position]);
    text.getLineCount();
    imageLoader.DisplayImage(data[position], image);
    return vi;
}

Здесь важно то, что происходит в методе onClick. Я хотел бы иметь параметр элемента, но это невозможно для этого OnClickListener. Это возможно для обычных списков, я это знаю.

Итак, как я могу определить, какой элемент нажат?

PS: я пытался подумать об использовании какого-то vi.setTag(<<number>>);, но я не понимаю, как это можно сделать, не установив один и тот же тег для всех элементов списка.


person gosr    schedule 05.02.2012    source источник
comment
Откуда номер артикула? Почему бы вам просто не использовать setTag с номером элемента, относящимся к конкретному виду?   -  person Cody    schedule 05.02.2012


Ответы (2)


Почему бы не использовать onItemClickListener в самом ListView?

Ваш адаптер должен содержать список одного типа объекта (строгих правил нет, но это значительно упрощает управление элементом). Например

class MsgObject{
    String msg;
    String data

    //TODO Getters/Setters goes here
}

Тогда ваш CustomAdapter будет содержать только

List<MsgObject> objectList;

Тогда ваш getView будет выглядеть примерно так

    MsgObject m = (MsgObject)getObject(position);
    TextView text=(TextView)vi.findViewById(R.id.text);
    ImageView image=(ImageView)vi.findViewById(R.id.image);
    text.setText(m.getMsg());
    text.getLineCount();
    imageLoader.DisplayImage(m.getData(), image);
    //Tag id is predefined in xml
    vi.setTag(R.id.listItemTag, m);
    return vi;

Теперь ваше представление будет обрабатывать это как один объект вместо одного макета с несколькими значениями.

Затем мы перемещаем все действия щелчка в действие, в котором находится ListView.

listView.setOnItemClickListener(){
    new AdapterView.OnItemClickListener(){

        @override
        public onItemClick(AdapterView<?> parent, View view, int position, long id){
            MsgObject m = (MsgObject)view.getTag(R.id.listItemTag);
            Toast.makeText(context, "Pos[" + position 
                + "] clicked, with msg: " + m.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

};

Именно так у меня есть ListView с отложенной загрузкой ImageView. Затем вы сможете получить доступ к объекту, привязанному к этому представлению, а также к позиции, по которой щелкнули.

Если вы так хотите разделить msg и data. Вы можете использовать setTag(id, obj); для обоих объектов и т. д.

setTag(R.id.listItemMsg, msg[position]);
setTag(R.id.listItemData, data[position]);

ОБНОВЛЕНИЕ: пример моего CustomAdapter

/**
 * Adapter for displaying Place selection list.
 * @author Poohdish Rattanavijai
 *
 */    
public class PlaceAdapter extends BaseAdapter {
    private static final String TAG = PlaceAdapter.class.getSimpleName();
    private List<PlaceVO> list; // <-- list of PlaceVOs
    private Context context;
    private int viewResourceId;

    /**
     * 
     * @param context Context
     * @param viewResourceId Layout ID for each item
     * @param list resource list to populate
     */
    public PlaceAdapter(Context context, int viewResourceId, List<PlaceVO> list){
        this.context = context;
        this.viewResourceId = viewResourceId;
        this.list = list;
    }

    /**
     * Number of result in the list plus one (for +add at the last item)
     */
    @Override
    public int getCount() {

        if(null != list){
            return list.size();
        }

        return 1;
    }

    @Override
    public Object getItem(int arg0) {
        if(null != list){
            try {
                return list.get(arg0);
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
//      if(null != list){
//          try {
//              return list.get(position).getId();
//          } catch (IndexOutOfBoundsException e) {
//              return 0;
//          }
//      }
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(null == convertView){
            /**
             * View does not exist, populate.
             */
            LayoutInflater inflater = LayoutInflater.from(this.context);
            convertView = inflater.inflate(this.viewResourceId, parent, false);
        }
        ViewHolder holder = (ViewHolder)convertView.getTag(R.id.adpter_view);

        if(null == holder){
            Log.d(TAG, "holder not found, init.");
            /**
             * ViewHolder does not exists for this view; create and assign respective view.
             */
            holder = new ViewHolder();
            holder.title = (TextView) convertView.findViewById(R.id.title);
            holder.details = (TextView) convertView.findViewById(R.id.details);
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);
            holder.progress = (ProgressBar) convertView.findViewById(R.id.progress);
        }

        PlaceVO v = (PlaceVO)getItem(position); // <-- GetItem

        if(null != v){
            Log.d(TAG, "Place not null");
            if(HelperUtil.IsNotNullOrEmpty(v.getName())){
                Log.d(TAG, "Name: " + v.getName());
                holder.title.setText(v.getName());
            }

            if(HelperUtil.IsNotNullOrEmpty(v.getVicinity())){
                Log.d(TAG, "details: " + v.getVicinity());
                holder.details.setText(v.getVicinity());
            }

            if(HelperUtil.IsNotNullOrEmpty(v.getIcon())){
                holder.progress.setVisibility(View.VISIBLE);
                holder.icon.setVisibility(View.GONE);
                            //TODO Initialize LazyLoad
            }else{
                holder.progress.setVisibility(View.VISIBLE);
                holder.icon.setVisibility(View.GONE);
            }
        }
            // Two tags, one for holder, another for the VO
        convertView.setTag(R.id.adpter_view, holder);
        convertView.setTag(R.id.adpter_object, v);
        return convertView;
    }

    static class ViewHolder{
        TextView title;
        TextView details;
        ImageView icon;
        ProgressBar progress;
    }
}

Внутри действия я обрабатываю действие щелчка элемента с помощью

OnItemClickListener itemClick = new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1,
                    int arg2, long arg3) {
                PlaceVO v = (PlaceVO)arg1.getTag(R.id.adpter_object); // <-- get object using tag.
                switchToPlaceScreen(v);
            }
        };
listView.setOnItemClickListener(itemClick);

Надеюсь, это ответ на ваш вопрос :)

person RobGThai    schedule 05.02.2012
comment
Спасибо - похоже, это может сработать. Не могли бы вы уточнить, как написать этот класс MsgObject? Я имею в виду, что вы не написали методы getMsg() и getData(). И где установлены фактические строки сообщений и данных? Кроме того, где нужно использовать objectList? - person gosr; 05.02.2012
comment
Вам понадобится геттер/сеттер, я просто опустил их для простоты. - person RobGThai; 06.02.2012
comment
Удалось заставить его работать с комбинацией кода, который вы разместили первым, и другого вашего примера. Ваше здоровье! - person gosr; 20.03.2012

text.setText(msg[position]);
text.getLineCount();
imageLoader.DisplayImage(data[position], image);

По мере того, как вы продвигаетесь к созданию пользовательского адаптера, вы хотите как можно больше ссылаться на свои элементы, используя getItem(int position). Старайтесь не ссылаться на него, используя фактические массивы, которые вы передаете в конструкторе адаптера.

Попробуйте переопределить getItem(int position) и getItemId(int position) на вашем адаптере и используйте их вместо самих массивов внутри вашего getView.

person josephus    schedule 05.02.2012