Как реализовать Android Pull-to-Refresh


433

В приложениях Android, таких как Twitter (официальное приложение), когда вы сталкиваетесь с ListView, вы можете опустить его (и он отскочит назад после освобождения), чтобы обновить содержимое.

Интересно, как, по вашему мнению, лучше всего это осуществить?

Некоторые возможности, о которых я мог подумать:

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

Любые рекомендации?

PS Интересно, когда будет опубликован официальный исходный код приложения Twitter. Было упомянуто, что это будет выпущено, но прошло 6 месяцев, и мы не слышали об этом с тех пор.


Является ли эта функциональность может быть реализована в динамически создаваемой TableLayout. Пожалуйста, помогите .....
Арун Бадоле

github.com/fruitranger/PulltorefreshListView - еще одна моя реализация. Плавная и сенсорная поддержка.
Чанвэй Яо

6
Связанный: «Pull-to-refresh»: шаблон анти-пользовательского интерфейса на Android - это статья, в которой утверждается, что этот пользовательский интерфейс не должен использоваться на Android, и вызвал много дискуссий о его целесообразности.
Blahdiblah

29
Кто-то должен сообщить об этом Google, потому что последняя версия Gmail для Android использует этот «анти-шаблон».
Ник

4
Pull для обновления - это (и уже давно) стандартный принятый шаблон в iOS и Android, и это очень естественно, так что это обсуждение анти-паттернов устарело. Пользователи ожидают увидеть это и вести себя как таковые.
Мартин Маркончини

Ответы:


312

Наконец, Google выпустил официальную версию библиотеки для обновления!

Он вызывается SwipeRefreshLayoutвнутри библиотеки поддержки, а документация здесь :

  1. Добавьте SwipeRefreshLayoutв качестве родительского представления, которое будет рассматриваться как обновление для обновления макета. (Я взял ListViewв качестве примера, это может быть любое Viewподобное LinearLayoutи ScrollViewт. Д.)

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/pullToRefresh"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ListView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </android.support.v4.widget.SwipeRefreshLayout>
  2. Добавить слушателя в свой класс

    protected void onCreate(Bundle savedInstanceState) {
        final SwipeRefreshLayout pullToRefresh = findViewById(R.id.pullToRefresh);
        pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refreshData(); // your code
                pullToRefresh.setRefreshing(false);
            }
        });
    }

Вы также можете позвонить pullToRefresh.setRefreshing(true/false);согласно вашему требованию.

ОБНОВИТЬ

Библиотеки поддержки Android устарели и были заменены на AndroidX. Ссылку на новую библиотеку можно найти здесь .

Также вам необходимо добавить следующую зависимость в ваш проект:

implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'

ИЛИ

Вы можете перейти к Refactor >> Migrate to AndroidX, и Android Studio будет обрабатывать зависимости для вас.


3
я могу использовать ProgressBar вместо цветовой схемы на SwipeRefreshLayout?
pablobaldez

54
1 апреля 14 года - и это была не шутка
aeracode


80

Я предпринял попытку реализовать компонент «Обновление до обновления», он далек от завершения, но демонстрирует возможную реализацию, https://github.com/johannilsson/android-pulltorefresh .

Основная логика реализована в PullToRefreshListViewтом, что расширяется ListView. Внутренне он управляет прокруткой представления заголовка с помощью smoothScrollBy(Уровень API 8). Теперь виджет обновлен с поддержкой 1.5 и более поздних версий, пожалуйста, прочтите README для поддержки 1.5.

В ваших макетах вы просто добавляете это так.

<com.markupartist.android.widget.PullToRefreshListView
    android:id="@+id/android:list"
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    />

3
Привет, Йохан, я скачал твой пример кода. Он работает в устройстве Android 2.2, но не работает в устройстве 2.1. Я думаю, потому что он использовал метод smoothScrollBy, который доступен только в 2.2 или позже, правильно? И есть ли у вас идея реализовать этот эффект в версии 2.1 или более ранней? Спасибо

Да, это верно, как я уже говорил в ответе, что smoothScrollBy был представлен в API Level 8 (2.2). Я еще не нашел правильный способ реализовать его для других версий, но он предполагает, что должна быть возможность портировать реализацию smoothScrollBy, но я думаю, что обсуждение следует вести на сайте проекта, а не при переполнении стека?
Йохан Берг Нильссон

Это специально, что идентификатор @ + id / android: list, а не @android: id / list, это выглядит странно? Проект выдает ошибку инфляции здесь, на моей стороне, в настоящее время я проверяю это ...
Матиас Конрадт

12
Это лучшая библиотека для обновления, которую я нашел .. github.com/chrisbanes/Android-PullToRefresh . Он работает с ListViews, GridViews и Webviews. Также имеет функцию Pull up для обновления шаблона.
rOrlig

1
Как добавить проект в папку библиотеки при работе с Intellij Idea? Кто-нибудь может мне помочь?
Мустафа Гювен

55

Я также реализовал надежную библиотеку PullToRefresh для Android с открытым исходным кодом, простую в использовании и настраиваемую. Вы можете заменить свой ListView на PullToRefreshListView, как описано в документации на странице проекта.

https://github.com/erikwt/PullToRefresh-ListView


Я перепробовал все реализации, перечисленные здесь, и ваша - лучшая, с точки зрения простого / чистого / плавного перетаскивания, чтобы обновить реализацию (нет нажатия, чтобы обновить странности, как у Йохана). Спасибо!
Гэди

1
Может ли это заменить стандартный ListView для работы с ListActivity, ListFragment и так далее?
davidcsb

Да, просто дайте PullToRefreshListView правильный идентификатор. В XML: android: id = "@ android: id / list"
Эрик,

1
это абсолютно потрясающе! Google привел меня сюда, и, просматривая примеры, выглядело проще всего реализовать. Браво и спасибо, сэр!
Эван Р.

Очень продуманный компонент, красивый и простой. Это определенно должен быть принятый ответ.
Адам

42

Я думаю, что самый простой способ - это предоставить библиотеку поддержки Android:

android.support.v4.widget.SwipeRefreshLayout;

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

  <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refresh"
        android:layout_height="match_parent"
        android:layout_width="match_parent">
    <android.support.v7.widget.RecyclerView
        xmlns:recycler_view="http://schemas.android.com/apk/res-auto"
        android:id="@android:id/list"
        android:theme="@style/Theme.AppCompat.Light"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/button_material_light"
        >

    </android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>

Я предполагаю, что вы используете представление рециркулятора вместо просмотра списка. Тем не менее, просмотр списка по-прежнему работает, поэтому вам просто нужно заменить recyclerview на просмотр списка и обновить ссылки в коде Java (фрагмент).

Во фрагменте своей деятельности вы сначала реализуете интерфейс SwipeRefreshLayout.OnRefreshListener: i, e

public class MySwipeFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener{
private SwipeRefreshLayout swipeRefreshLayout;

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_item, container, false);
        swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh);
        swipeRefreshLayout.setOnRefreshListener(this);
}


 @Override
  public void onRefresh(){
     swipeRefreshLayout.setRefreshing(true);
     refreshList();
  }
  refreshList(){
    //do processing to get new data and set your listview's adapter, maybe  reinitialise the loaders you may be using or so
   //when your data has finished loading, cset the refresh state of the view to false
   swipeRefreshLayout.setRefreshing(false);

   }
}

Надеюсь, это поможет массам


Это работает хорошо. Тем не менее, есть одна вещь, касающаяся setRefreshing: «Не вызывайте это, когда обновление инициируется жестом смахивания», как описано в developer.android.com/reference/android/support/v4/widget/…
gabriel14

Отличная работа! Спасибо.
technik

22

В этой ссылке вы можете найти ответвление известного PullToRefreshпредставления, в котором есть новые интересные реализации, такие как PullTorRefreshWebViewили, PullToRefreshGridViewили возможность добавить PullToRefreshнижний край списка.

https://github.com/chrisbanes/Android-PullToRefresh

И лучшее из того, что идеально работает в Android 4.1 (нормальное PullToRefreshне работает)


3
Большой комментарий в верхней части readme указывает: ЭТОТ ПРОЕКТ НЕ ПОДДЕРЖИВАЕТСЯ. Так или иначе, эта библиотека - лучшая, которую я когда-либо видел! Отличная работа!
Милтон

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

18

У меня есть очень простой способ сделать это, но теперь я уверен, что это надежный способ. Есть мой код PullDownListView.java

package com.myproject.widgets;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;

/**
 * @author Pushpan
 * @date Nov 27, 2012
 **/
public class PullDownListView extends ListView implements OnScrollListener {

    private ListViewTouchEventListener mTouchListener;
    private boolean pulledDown;

    public PullDownListView(Context context) {
        super(context);
        init();
    }

    public PullDownListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PullDownListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        setOnScrollListener(this);
    }

    private float lastY;

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            lastY = ev.getRawY();
        } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
            float newY = ev.getRawY();
            setPulledDown((newY - lastY) > 0);
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (isPulledDown()) {
                        if (mTouchListener != null) {
                            mTouchListener.onListViewPulledDown();
                            setPulledDown(false);
                        }
                    }
                }
            }, 400);
            lastY = newY;
        } else if (ev.getAction() == MotionEvent.ACTION_UP) {
            lastY = 0;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        setPulledDown(false);
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
    }

    public interface ListViewTouchEventListener {
        public void onListViewPulledDown();
    }

    public void setListViewTouchListener(
            ListViewTouchEventListener touchListener) {
        this.mTouchListener = touchListener;
    }

    public ListViewTouchEventListener getListViewTouchListener() {
        return mTouchListener;
    }

    public boolean isPulledDown() {
        return pulledDown;
    }

    public void setPulledDown(boolean pulledDown) {
        this.pulledDown = pulledDown;
    }
}

Вам просто нужно реализовать ListViewTouchEventListener в вашей деятельности, где вы хотите использовать этот ListView и установить слушателя

У меня это реализовано в PullDownListViewActivity

package com.myproject.activities;

import android.app.Activity;
import android.os.Bundle;

/**
 * @author Pushpan
 *
 */
public class PullDownListViewActivity extends Activity implements ListViewTouchEventListener {

    private PullDownListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        listView = new PullDownListView(this);
        setContentView(listView);
        listView.setListViewTouchListener(this);

        //setItems in listview
    }

    public void onListViewPulledDown(){
        Log.("PullDownListViewActivity", "ListView pulled down");
    }
}

Меня устраивает :)


18

Для реализации Android Pull-to-Refresh попробуйте этот кусок кода,

<android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/pullToRefresh"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

</android.support.v4.widget.SwipeRefreshLayout>

Класс деятельности:

ListView lv = (ListView) findViewById(R.id.lv);
SwipeRefreshLayout pullToRefresh = (SwipeRefreshLayout) findViewById(R.id.pullToRefresh);


lv.setAdapter(mAdapter);

pullToRefresh.setOnRefreshListener(new OnRefreshListener() {

        @Override
        public void onRefresh() {
            // TODO Auto-generated method stub

            refreshContent();

        }
    });



private void refreshContent(){ 

     new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                pullToRefresh.setRefreshing(false);
            }
        }, 5000);

 }

1
Спасибо. Это действительно помогло мне
Арпан Шарма

9

Никто не упомянул новый тип «Подтянуть для обновления», который отображается в верхней части панели действий, как в приложении Google Now или Gmail.

Есть библиотека ActionBar-PullToRefresh, которая работает точно так же.


7

Обратите внимание, что при реализации на Android и WP возникают проблемы с UX.

«Отличный показатель того, почему дизайнеры / разработчики не должны реализовывать функцию« pull-to-refresh »в стиле приложений iOS, - это то, как Google и их команды никогда не используют функцию« pull-to-refresh »на Android, пока они используют ее в iOS».

https://plus.google.com/109453683460749241197/posts/eqYxXR8L4eb


3
Я знаю, что этому больше года, но в приложении Gmail используется функция pull-to-refresh. Однако это не совсем то же самое с точки зрения пользовательского интерфейса, список не прокручивается вниз, скорее, новый вид отображается в верхней части экрана.
Ашишдух

4

Если вы не хотите, чтобы ваша программа выглядела как программа для iPhone, которая принудительно встроена в Android, стремитесь к более естественному внешнему виду и сделайте что-то похожее на Gingerbread:

альтернативный текст


2
@Rob: Я имел в виду оранжевую тень в верхней части списка, когда вы перебиваете, вместо того, чтобы отскочить, как на iPhone. Это подразумевается как комментарий (не ответ), но комментарии не могут иметь изображения.
Ли Райан

Ааа, извините, отличная идея. Я еще не играл с пряниками, поэтому не видел эффекта. Все еще жду Google, чтобы накатить его на нексус.
Роб

9
У меня есть пряники и оранжевое свечение отлично работает, когда список статичен. Но pull-down-refresh - это отличный механизм пользовательского интерфейса для обновления динамического списка. Хотя это распространено в мире iOS, это трюк с пользовательским интерфейсом, который не кажется странным в экосистеме Android. Я настоятельно рекомендую вам проверить это в официальном приложении Twitter. :)
Тьерри-Дмитрий Рой

Собственное поведение здесь означает, что вы достигли конца списка. Принятие этого и выполнение обновления идентично не родному, поскольку вы не делаете то, что делает платформа. Это означает, что вы, скорее всего, удивите пользователя. Я, конечно, не ожидал и не хотел обновлять только потому, что я попал в конец списка. Потяните вниз, чтобы освежить, намного более интуитивно понятный и понятный. И поскольку его использует нативное приложение для твиттера, я думаю, справедливо будет сказать, что это концепция пользовательского интерфейса, с которой знакомо большое количество людей.
steprobe

4

Я написал компонент для обновления для обновления здесь: https://github.com/guillep/PullToRefresh. Это событие работает, если в списке нет элементов, и я протестировал его на> = 1.6 телефонах Android.

Любое предложение или улучшение приветствуется :)



1

Чтобы получить последнюю версию Lollipop Pull-To Refresh:

  1. Загрузите последнюю версию библиотеки поддержки Lollipop SDK и Extras / Android
  2. Установите для Project Build Target значение Android 5.0 (в противном случае пакет поддержки может иметь ошибки с ресурсами)
  3. Обновите вашу libs / android-support-v4.jar до 21-й версии
  4. Используйте android.support.v4.widget.SwipeRefreshLayoutплюсandroid.support.v4.widget.SwipeRefreshLayout.OnRefreshListener

Подробное руководство можно найти здесь: http://antonioleiva.com/swiperefreshlayout/

Плюс к ListView рекомендую прочитать canChildScrollUp()в комментариях;)


Плюс один только для упоминания canChildScrollUp (). Очень важно!
Petro

1

Очень интересный Pull-to-Refresh от Yalantis . Gif для iOS, но вы можете проверить это :)

<com.yalantis.pulltorefresh.library.PullToRefreshView
android:id="@+id/pull_to_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
    android:id="@+id/list_view"
    android:divider="@null"
    android:dividerHeight="0dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />


0

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

Вот учебник, который демонстрирует, как реализовать Android Pull для обновления. Надеюсь, это поможет.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.