Яка мета методів getItem та getItemId у класі Android BaseAdapter?


155

Мені цікаво призначення методів getItemта getItemIdклас Адаптер в Android SDK.

З опису виходить, що getItemслід повернути основні дані. Отже, якщо у мене є масив імен ["cat","dog","red"]і я створюю адаптер aза допомогою цього, то a.getItem(1)повинен повернути "собаку", правильно? Що слід a.getItemId(1)повернути?

Якщо ви використали ці методи на практиці, чи могли б ви навести приклад?


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 все ще матиме мету? якщо так, що потрібно повернути? посада?
андроїд розробник

1
мета або використання методу в основному залежить від розробника і не пов'язане з додатком, керованим базою даних. використовуйте його в своїх інтересах для створення чіткого / читабельного / багаторазового коду.
Джеймс

1
Так, я здогадуюсь getView, getCount, getViewTypeCountІ т.д. використовуються спеціально для правильно показуючи свій ListView UI. інші функції просто допомагають створити інші функції, такі як виконання подальших дій при натисканні предмета тощо, хоча я часто використовую getItemвсерединіgetView
Джеймс

1
@NicolasZozol Звичайно - це безпечно не впроваджувати getItemId, просто повертати 0Lабо nullне використовувати його ніде. Я не бачу жодної очевидної причини, чому UUID був би більш цінним, ніж просто якесь longзначення для ID. Відключений режим? Що це?
Джеймс

1
@binnyb: Ніколас мав на увазі, що за допомогою UUIDs все одно можна створити дійсні унікальні ідентифікатори (наприклад, на мобільному пристрої), навіть не маючи мережевого з'єднання.
Левіт

32

Що ж, схоже, що на це питання можна було відповісти більш простим та прямим способом ... :-)

Простіше кажучи, Android дозволяє прикріпити longбудь-який ListViewпредмет, це так просто. Коли система повідомляє вас про вибір користувача, ви отримуєте три ідентифікуючі змінні, щоб повідомити, що було вибрано:

  • посилання на сам вид,
  • її числове положення у списку,
  • це longви приєднали до окремих елементів.

Ви самі вирішуєте, який із цих трьох для вас найпростіший для вирішення у вашому конкретному випадку, але у вас є все три, які можна вибрати з усіх часів. Подумайте про це longяк тег, який автоматично приєднується до елемента, лише те, що його ще простіше і легше читати.

Нерозуміння того, що він зазвичай робить, випливає з простої конвенції. Усі адаптери повинні забезпечити getItemId()рівне, навіть якщо вони фактично не використовують цю третю ідентифікацію. Отже, за умовою, ці адаптери (включаючи багато зразків у SDK або у всіх мережах Інтернету) просто повертаються positionз однієї причини: це завжди унікально. Однак, якщо адаптер повертається position, це дійсно означає, що він зовсім не хоче використовувати цю функцію, оскільки positionце все одно відомо.

Отже, якщо вам потрібно повернути будь-яке інше значення, яке ви вважаєте за потрібне, сміливо робіть це:

@Override
public long getItemId(int position) {
  return data.get(position).Id;
}

1
Приємне пояснення цього getItemId()... Що станеться, коли / якщо цей метод не буде замінено у вашому користувальницькому адаптері?
дентекс

Позначившись абстрактним у базовому класі, ви повинні. Якщо, звичайно, ви не перекриєте щось, що перекриває оригінальний адаптер. Спробуйте залишити це, і якщо Eclipse скаржиться, вам доведеться. :-)
Габор

Дякую. Я завжди коментував цей метод без попереджень. У мене CustomAdapter розширює ArrayAdapter <CustomListItem> за допомогою getCount (), getItem (...) та getView (...), використовуючи "шаблон власника". Тільки з цікавості ...
дентекс

Так, ви можете це зробити, тому що ArrayAdapter розширює BaseAdapter і вже забезпечує власну реалізацію.
Габор

І з простим масивом це все в порядку. Але просто розглянемо інший випадок, коли, наприклад, потрібно відобразити елементи з бази даних. Потім, ймовірно, ви продовжите BaseAdapter, і ви можете використовувати цей довгий ідентифікатор для зберігання ключа бази даних. Коли користувач щось вибере, ви безпосередньо повернете ключ обраного запису через аргумент id . Потім ви можете завантажити його, наприклад, з бази даних, наприклад. Єдина проблема, що вам доведеться використовувати цифрові клавіші, оскільки Android вирішив довго, а не щось ширше.
Габор

6

getItemIdМетод в основному призначений для роботи з курсором , які спираються на SQLite бази даних. Він поверне нижнє поле ідентифікатора курсора для елемента в позиції 1.

У вашому випадку для елемента в позиції 1 немає ідентифікатора: я припускаю, що реалізація ArrayAdapter просто повертає -1 або 0.

EDIT: насправді він просто повертає позицію: у цьому випадку 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.


3
Це дійсно надзвичайно корисно, якщо у вашому списку списку є заголовок, а позиції, передані обробнику кліків, відключені одним
ентропія

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 та заголовка, а якщо ви зміните потім і зателефонуєте notifyDataSetChanged()на свій адаптер, то адаптер зателефонує методу getItemId () кожного елемента та оновить лише ті елементи, ідентифікатор яких змінився.

Це дуже корисно, якщо ви робите якісь "важкі" операції у вашому getView().

BTW: якщо ви хочете, щоб це працювало, вам потрібно переконатися, що ваш hasStableIds()метод повертає помилкове значення;


Це цінне спостереження, чи можете ви надати деякі дані для підтримки цього селективного механізму оновлення?
Хайме Агудо

Чому слід hasStableIds()повертати помилкове? Мені здається, що хеш-код, обчислений з одного і того ж рядка, кожен раз повертав би одне і те ж значення, що є стабільним ідентифікатором згідно з документами .
Big McLargeHuge

врахуйте, що використання hashCode може повернути істину до двох рядків
htafoya

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);
    }
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.