Каково назначение методов getItem и getItemId в классе Android BaseAdapter?

155

Мне любопытно о цели методов getItemиgetItemId класса Adapter в Android SDK.

Из описания, похоже, getItemследует возвращать исходные данные. Итак, если у меня есть массив имен, ["cat","dog","red"]и я создаю адаптер, aиспользуя это, то a.getItem(1)должен вернуть «собаку», правильно? Что должно a.getItemId(1)вернуться?

Если вы использовали эти методы на практике, не могли бы вы привести пример?

oneporter
источник
16
+1 Отличный вопрос. Я хочу отметить, что getItemId()в ArrayAdapter()всегда возвращается -1сassert false : "TODO"; return -1;
Rds

Ответы:

86

Я вижу эти методы как более чистый подход к доступу к данным моего списка. Вместо прямого доступа к моему объекту адаптера через что-то вроде этого myListData.get(position)я могу просто вызвать адаптер как adapter.get(position).

То же самое касается getItemId. Обычно я использую этот метод, когда хочу выполнить какую-то задачу на основе уникального идентификатора объекта в списке. Это особенно полезно при работе с базой данных. Возвращенныйid может быть ссылка на объект в базе данных, с которым я затем мог бы выполнять различные операции (обновление / удаление / и т. Д.).

Таким образом, вместо доступа к идентификатору из необработанного объекта данных, который myListData.get(position).getId()вы можете использовать adapter.getItemId(position).

Одним из примеров того, где я чувствовал, что мне нужно было использовать эти методы, был проект, использующий SeparatedListViewAdapter . Этот адаптер может содержать несколько различных типов адаптеров, каждый из которых представляет данные различного типа (обычно). При звонке getItem(position)на SeparatedListViewAdapterобъект возвращается , может отличаться в зависимости от «раздела» позиция в том , что вы посылаете его.

Например, если у вас 2 разделов в списке (фруктов и конфеты): Если вы использовали getItem(position)и positionоказались на элементе в фруктовом разделе, вы получите другой объект , чем если бы вы запросили getItem(position)с positionуказывающим на элемент в карамели раздел. Затем вы можете вернуть какое-то постоянное значение идентификатора, в getItemId(position)котором отображается тип getItem(position)возвращаемых данных , или использовать instanceofдля определения того, какой объект у вас есть.

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

Джеймс
источник
7
для адаптеров, не связанных с sql, getItemId будет по-прежнему иметь цель? если так, что должно быть возвращено? позиция?
Android-разработчик
1
цель или использование метода в основном зависит от разработчика и не привязана к приложению, управляемому базой данных. используйте его в своих интересах, чтобы создать понятный / читаемый / повторно используемый код.
Джеймс
1
Да, я полагаю. getView, getCount, getViewTypeCountИ т.д. используются специально для правильно показывая свой ListView UI. другие функции просто помогают создавать и реализовывать другие функции, такие как выполнение дальнейших действий при нажатии на элемент и т. д., хотя я часто использую их getItemвнутриgetView
Джеймс
1
@NicolasZozol Конечно, безопасно не внедрять getItemId, просто возвращать 0Lили nullи нигде не использовать. Я не вижу очевидной причины, по которой UUID был бы более ценным, чем просто какое-то longзначение для ID. Отключен режим? Что это такое?
Джеймс
1
@binnyb: Николас имел в виду, что с помощью UUID все еще можно создавать действительные уникальные идентификаторы (например, на вашем мобильном устройстве), даже без подключения к сети.
Левит
32

Что ж, похоже, на этот вопрос можно было бы ответить проще и проще ... :-)

Проще говоря, Android позволяет прикрепить longк любомуListView элементу, это так просто. Когда система уведомляет вас о выборе пользователя, вы получаете три идентифицирующие переменные, чтобы сказать вам, что было выбрано:

  • ссылка на само представление,
  • его числовая позиция в списке,
  • этот long вы прикрепили к отдельным элементам.

Вы сами решаете, какой из этих трех вариантов проще всего решать в вашем конкретном случае, но у вас есть все три варианта на выбор. Думай об этомlong как о теге, автоматически прикрепленном к элементу, только в том случае, если он еще проще и легче считывается.

Непонимание того, что обычно происходит, основано на простом соглашении. Все адаптеры должны предоставить getItemId()даже если они на самом деле не используют эту третью идентификацию. Таким образом, по соглашению, эти адаптеры (включая множество в примерах в SDK или по всему Интернету) просто возвращаются positionпо одной причине: они всегда уникальны. Тем не менее, если адаптер возвращается position, это действительно означает, что он вообще не хочет использовать эту функцию, так какposition как в любом случае он уже известен.

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

@Override
public long getItemId(int position) {
  return data.get(position).Id;
}
Габор
источник
1
Хорошее объяснение этому getItemId()... Что произойдет, если / если этот метод не переопределен в вашем пользовательском адаптере?
dentex
Вы должны быть помечены как абстрактные в базовом классе. Если, конечно, вы не переопределите то, что переопределяет оригинальный адаптер. Попытайтесь оставить это, и если Затмение жалуется, тогда вы должны. :-)
Габор
Спасибо. Я всегда комментировал этот метод без предупреждений. У меня CustomAdapter расширяет ArrayAdapter <CustomListItem> с помощью getCount (), getItem (...) и getView (...), используя «шаблон держателя». Просто из любопытства ...
dentex
Да, вы можете сделать это, потому что ArrayAdapter расширяет BaseAdapter и уже предоставляет свою собственную реализацию.
Габор
И с простым массивом, все в порядке. Но просто рассмотрим другой случай, когда вы хотите отобразить элементы из базы данных, например. Затем вы, вероятно, расширите BaseAdapter, и вы сможете использовать этот длинный идентификатор для хранения ключа базы данных. Когда пользователь выбирает что-то, вы напрямую возвращаете ключ выбранной записи через аргумент id . Например, вы можете сразу загрузить его из базы данных. Единственная проблема в том, что вы должны использовать цифровые клавиши, потому что Android выбрал длинный вместо чего-то более широкого.
Габор
6

Этот getItemIdметод в основном предназначен для работы с курсорами, поддерживаемыми базами данных SQLite. Он вернет поле id основного курсора для элемента в позиции 1.

В вашем случае нет идентификатора для элемента в позиции 1: я предполагаю, что реализация ArrayAdapter просто возвращает -1 или 0.

РЕДАКТИРОВАТЬ: на самом деле, он просто возвращает позицию: в этом случае 1.

Фей
источник
2
Нет, это возвращается -1. Вот реализацияassert false : "TODO"; return -1;
rds
5
Начиная с Android 4.1.1, он возвращает позицию: grepcode.com/file/repository.grepcode.com/java/ext/…
emmby
4

Я хотел бы отметить , что после внедрения getItemи getItemIdвы можете использовать ListView.getItemAtPosition и ListView.getItemIdAtPosition для прямого доступа к вам данных, вместо того , чтобы идти через адаптер. Это может быть особенно полезно при реализации слушателя onClick.

leo9r
источник
3
Это действительно очень полезно , если у вас есть заголовок на вашей ListView и позиции передаются щелчок обработчик выключены один
энтропия
4

Если вы реализуете getItemIdправильно, то это может быть очень полезно.

Пример :

У вас есть список альбомов:

class Album{
     String coverUrl;
     String title;
}

И вы реализуете getItemIdтак:

@Override
public long getItemId(int position){
    Album album = mListOfAlbums.get(position);
    return (album.coverUrl + album.title).hashcode();
}

Теперь ваш идентификатор элемента зависит от значений полей coverUrl и title, и если вы измените его и notifyDataSetChanged()вызовете свой адаптер, то адаптер вызовет метод getItemId () каждого элемента и обновит только те элементы, идентификатор которых изменился.

Это очень полезно, если вы выполняете некоторые «тяжелые» операции в вашем getView().

Кстати: если вы хотите, чтобы это работало, вы должны убедиться, что ваш hasStableIds() метод возвращает false;

Данило Волох
источник
Это ценное наблюдение, можете ли вы предоставить некоторые данные для поддержки этого механизма выборочного обновления?
Хайме Агудо
Почему должен hasStableIds()возвращать false? Мне кажется, что хеш-код, вычисленный из одной и той же строки, будет возвращать одно и то же значение каждый раз, что является стабильным идентификатором в соответствии с документами .
Big McLargeHuge
Учтите,
2

getItemили getItemIdнесколько методов, предназначенных в основном для прикрепления данных с элементами в списке. В случае getItem, вы можете передать любой объект, который будет прикреплен к элементу в списке. Обычно люди возвращаются null. getItemIdлюбое уникальное longзначение, которое вы можете прикрепить к тому же элементу в списке. Люди обычно возвращают позицию в списке.

Какая польза. Ну, так как эти значения привязаны к элементу в списке, вы можете извлечь их, когда пользователь нажимает на элемент. Эти значения доступны через AdapterViewметоды.

// template class to create list item objects
class MyListItem{
    public String name;
    public long dbId;

    public MyListItem(String name, long dbId){
        this.name = name;
        this.dbId = dbId;
    }
}

///////////////////////////////////////////////////////////

// create ArrayList of MyListItem
ArrayList<MyListItem> myListItems = new ArrayList<MyListItem>(10);

// override BaseAdapter methods
@Override
public Object getItem(int position) {
    // return actual object <MyListItem>
    // which will be available with item in ListView
    return myListItems.get(position);
}

@Override
public long getItemId(int position) {
    // return id of database document object
    return myListItems.get(position).dbId;
}

///////////////////////////////////////////////////////////

// on list item click, get name and database document id
my_list_view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        // extract item data
        MyListItem selectedItem = (MyListItem)parent.getItemAtPosition(position);      
        System.out.println("Your name is : " + selectedItem.name);

        // extract database ref id
        long dbId = id;

        // or you could also use
        long dbId = parent.getItemIdAtPosition(position);
    }
});
Uday Hiwarale
источник