Pull to refresh

SwipeRefreshLayout: не сферический и не в вакууме

Reading time3 min
Views13K
Про новый SwipeRefreshLayout из библиотеки Android support на Хабре уже писали, да и Google любезно выдаёт множество ссылок на подобные примеры. Их всех объединяет одно — в SwipeRefreshLayout добавляется единственное TextView или ListView, и через минуту разработчик с умилением глядит на работающую анимацию. А если нам интерфейс чуть посложнее надо?

Итак, у нас есть Activity с двумя элементами: сверху — панель фильтра, снизу — некое View (лишние теги удалены):

<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <fragment
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <com.mycompany.MyView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

Нам всего-то нужно заменить LinearLayout на SwipeRefreshLayout. Точнее, просто заменить не получится — SwipeRefreshLayout позволяет иметь только один вложенный элемент. Давайте просто обернём LinearLayout в SwipeRefreshLayout:

<android.support.v4.widget.SwipeRefreshLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
        <fragment
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
        <com.mycompany.MyView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>

Запускаем — работает! Правда, как-то странно работает: индикатор обновления то не появляется, то пропадает.

Материмся, гуглим, изучаем опыт предыдущих поколений. Оказывается, в SwipeRefreshLayout что попало класть нельзя. ScrollView можно, GridView можно, ListView можно, а больше ничего и нельзя. ListView и GridView нам совсем не подходят, а вот ScrollView можно попробовать. Оборачиваем наш многострадальный LinearLayout в ScrollView:

<android.support.v4.widget.SwipeRefreshLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
            <fragment android:layout_width="match_parent"
                android:layout_height="wrap_content" />
            <com.mycompany.MyView
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </LinearLayout>
    </ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>

Обращаем внимание — Lint тут же требует изменить android:layout_height с match_parent на wrap_content у вложенных в ScrollView элементов.

Запускаем, и… видим только панель фильтра. А где же нижний View? А нет его. Точнее, он есть, но мы его не видим. Раньше LinearLayout растягивал его на всё свободное пространство благодаря match_parent, но теперь-то мы внутри ScrollView! А ScrollView потому и ScrollView, что никого внутри себя растягивать не собирается, потому что его содержимое может выходить за пределы самого ScrollView.

Выход — либо наше нижнее MyView должно иметь явную высоту (через android:minHeight или onMeasure()), либо у ScrollView должен быть установлен атрибут android:fillViewport=«true», который и заставит его растянуть нижний View.

<ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true" >

Гламурных всем анимаций!
Tags:
Hubs:
Total votes 15: ↑12 and ↓3+9
Comments4

Articles