Используем стандартные элементы ListFragment по назначению

В одном из проектов столкнулся с тем, что в приложении нужно было отображать списки результатов для различных запросов (поиск по словам, датам, тегам и т.п.). Так как списки повторялись в разных Activity, самым очевидным решением было использовать фрагменты, а конкретно создать свой класс ListFragment и повторно использовать его в проекте.

image

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

Прежде, чем начать работать с ListFragment, давайте немного изучим, что же находится внутри?


По умолчанию используется разметка из ~\sdk\platforms\android-XX\data\res\layout\list-content.xml
У некоторых представлений этой разметки имеются идентификаторы, благодаря которым мы можем кастомизировать родительский контейнер, список и текст пустого списка.

Если пробежаться по исходникам класса ListFragment, мы увидим:
View mListContainer; // родительский контейнер (идентификатор R.id.listContainer)
final private AdapterView.OnItemClickListener mOnClickListener // "слушатель" клика на элемент списка
ListAdapter mAdapter; // адаптер списка
ListView mList; // представление списка (идентификатор android.R.id.list)
TextView mEmptyView; // текстовое представление для случая пустого списка, будем называть его empty-текст (идентификатор android.R.id.empty)
View mProgressContainer; // представление для анимации загрузки


Итак, создадим наш класс:
import android.app.ListFragment;

public class MyListFragment extends ListFragment{
}


И включим его в разметку нашего Activity (либо можно установить фрагмент программно)
<fragment
        android:name="**.*******.********.MyListFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
</fragment>


Запускаем приложение, переходим к нашему Activity и сразу же видим анимацию загрузки:
image

Теперь, для того, чтобы скрыть анимацию и отобразить список, нам достаточно установить адаптер вызвав метод setAdapter(ListAdapter adapter). В этом проекте я использовал Loader в Activity и OttoBus событие, но вариантов передать объект списка во фрагмент предостаточно, поэтому прошу не обращать внимание на сам метод.
@Subscribe
public void onMyEvent(MyEvent loaderEvent) throws InterruptedException {
        String[] listItems = {"item 1", "item 2", "item 3","item 4"}; // пример списка
        Thread.sleep(2000); // имитация времени загрузки
        ArrayAdapter mArrayAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, listItems); // адаптер списка
        setListAdapter(mArrayAdapter); // установка адаптера
}


Сразу после установки адаптера анимация исчезнет и мы увидим:
image

А если наш список вдруг окажется пустым, то получим другой экран:
image

Для установки своего empty-текста используется метод:
setEmptyText(getResources().getString(R.string.error));


Также в классе ListFragment имеется метод обработки нажатий списка, который мы можем переопределить
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
     // ваш ход господа
}


Вы можете не использовать стандартную разметку, и задать свою, но не забудьте указать правильные идентификаторы вашим представлениям.

Итого. ListFragment имеет свои представления и логику работы.
Надеюсь эта статья позволит вам принять оценить, достаточно ли вам встроенной реализации или же нужно писать свою.
P.S. Лично мне понравилась простота и скорость внедрения в проект, где я смог успешно использовать один класс фрагмента сразу в 4х различных Activity.

Плюсы:
* Не обязательно создавать XML-разметку.
* Нет необходимости объявлять представления в вашем классе.
* Не нужно реализовывать логику показа/скрытия анимации, списка и empty-текста.
* Не нужна установка «слушателя»
* Меньше кода.

Минусы:
* Нужно изучить методы класса ListFragment и как все работает.
* Нельзя изменить встроенную анимацию даже через собственную разметку.
* Нельзя переопределить логику скрытия/показа анимации, списка и empty-текста.

P.S. Присоединяйтесь к чату русскоязычных Android разработчиков.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 13

    +3
    Минусы:
    * Нельзя добавить свое представление;
    * Сложнее менять стили;
    * Прочие минусы кастомизации (такое решение подойдет для простых списков, без Refresh, подгрузок и прочего...).

    Можно. Достаточно переопределить onCreateView и в layout проставить листу android.R.id.list в ид. Можно даже добавить empty view с идом android.R.is.empty.

    <FrameLayout>
       <ListView android:id="@android:id/list"/>
       <TextView android:id="@android:id/empty/>
    </FrameLayout>
    


      0
      Ок, постараюсь сегодня дописать про это.
      0
      Хорошая статия.
      Есть вопрос, я придерживаюсь принципу что у всех мойх активити и фрагментов есть родителский класс BaseActivity и BaseFragment. Что исключает вазможность использования ListFragment и т.д. Но хочется :) Есть какой нибудь решение при таких обстоятелствах?
        0
        Отнаследовать ListFragment от BaseFragment? :)
          0
          Не, я хочу использовать тот ListFragment, который уже есть в Andeoid API :) Но не могу унаследовать мой фрагменты от ListFragment, так как они унаследуется от BaseFragment.
            0
            Понял. Я когда-то для таких целей делал BaseFragment и BaseListFragment, а их общую логику выносил композицией.
          0
          Есть, по моему проблем с ListFragment больше, чем плюсов, так как у списка может быть еще состояние «Ошибка при получении данных». Конечно, это решаемая проблема, но решаемая как то неудобно и неприятно. Лично я даже сделал библиотечку для этого, брал исходную реализацию, переделывал для нее апи и выносил все это в отдельный виджет, которым можно обернуть что угодно, хоть список, хоть часть экрана. Вьюхи же для состояний просто задавались через xml. И таких библиотек немало. По личному опыту, такой подход намного более гибкий.
            0
            Зависит от проекта. Например в другом приложении, я отказался от ListFragment, и реализовал свою логику и представления.
              0
              Ну, все зависит от проекта.
            0
            Я как-то писал приложение под Android на Scala с использованием вот этого плагина, и там я подобную проблему решил с применением trait'ов.
            Каких-то решений с использованием Java я не знаю, увы.
            0
            А в чем преимущество перед ListView?
              +1
              Не совсем правильно сравнивать их, ListView все-таки базовый класс и его далеко не всегда можно будет заменить ListFragment-ом.

              Но повторю еще раз то, что уже писал в статье: с этим фрагментом у вас уже есть адаптер и «слушатель», методы которых вы можете сразу использовать без инициализации (более того, вам не нужно заботиться об их восстановлении/обнулении), а также у вас есть анимация с empty-текстом, которые используются для разных состояний списка.
              0
              Используя данный подход в своем приложении столкнулся с трудностью: во время добавления хидера к списку вся разметка хидера корежится и сжимается до размеров элемента списка. Может ли быть причина в определенных ограничениях ListFragment?

              Only users with full accounts can post comments. Log in, please.