SwiperefreshLayout в Android

Я использую SwipeRefreshLayout в моем макете ниже:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="@color/homePageBackground"
  android:orientation="vertical" >


<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/swipeRefreshLayout_listView"
    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:id="@+id/announcementHomefragment"
        android:name="in.test.app.AnnouncementFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/homePageBackground" >

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="15dp"
                android:background="@color/homePageBackground" >

                <TextView
                    android:id="@+id/newsTitle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_marginLeft="15dp"
                    android:layout_marginTop="5dp"
                    android:gravity="center"
                    android:text="@string/new_list"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:textColor="@color/white"
                    android:textStyle="bold" />

                <fragment
                    android:id="@+id/newshomefragment"
                    android:name="in.test.app.NewsFragment"
                    android:layout_width="wrap_content"
                    android:layout_height="190dp"
                    android:layout_below="@id/newsTitle"
                    android:layout_marginTop="-15dp" />

                <TextView
                    android:id="@+id/productTitle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/newshomefragment"
                    android:layout_gravity="center"
                    android:layout_marginLeft="15dp"
                    android:layout_marginTop="5dp"
                    android:gravity="center"
                    android:text="@string/product_in_home"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:textColor="@color/white"
                    android:textStyle="bold" />

                <fragment
                    android:id="@+id/proCategoryhomefragment"
                    android:name="in.test.app.CategoryFragment"
                    android:layout_width="wrap_content"
                    android:layout_height="170dp"
                    android:layout_below="@id/productTitle"
                    android:layout_marginTop="-15dp" />

                <TextView
                    android:id="@+id/trainingTitle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/proCategoryhomefragment"
                    android:layout_gravity="center"
                    android:layout_marginLeft="15dp"
                    android:layout_marginTop="2dp"
                    android:gravity="center"
                    android:text="@string/trainings_in_home"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:textColor="@color/white"
                    android:textStyle="bold" />

                <fragment
                    android:id="@+id/trainingfragment"
                    android:name="in.test.app.TrainingFragment"
                    android:layout_width="match_parent"
                    android:layout_height="180dp"
                    android:layout_below="@id/trainingTitle"
                    android:layout_marginBottom="10dp"
                    android:layout_marginTop="-15dp" />
            </RelativeLayout>
        </ScrollView>
    </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>

When I pull down my SwipeRefreshLayout it is working, but as you can see in the above code I have a scroll view inside that. So when I am pulling down my scroll view, it goes down and half the images are not showing because it came down. When I am trying to pull up again my scroll view is not going up. Instead, SwipeRefreshLayout is getting call. What should i do?

Пожалуйста, помогите мне.




Ответы (7)


Я бы сказал, что лучше иметь расширенный SwipeRefreshLayout со слушателем, чтобы иметь возможность добавлять различные условия из классов, отображающих этот макет.

Примерно так: GeneralSwipeRefreshLayout.java

public class GeneralSwipeRefreshLayout extends SwipeRefreshLayout {   
  private OnChildScrollUpListener mScrollListenerNeeded;

  public interface OnChildScrollUpListener {
    boolean canChildScrollUp();
  }

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

 /**
  * Listener that controls if scrolling up is allowed to child views or not
  */
  public void setOnChildScrollUpListener(OnChildScrollUpListener listener) {
    mScrollListenerNeeded = listener;   
  }

  @Override
  public boolean canChildScrollUp() {
    if (mScrollListenerNeeded == null) {
      Log.e(GeneralSwipeRefreshLayout.class.getSimpleName(), "listener is not defined!");
    }
    return mScrollListenerNeeded != null && mScrollListenerNeeded.canChildScrollUp();
  }
}

А затем внутри вашего класса, который отображает SwipeRefreshLayout, содержащий макет ListView или GridView, вы можете сделать что-то вроде этого:

mSwipeLayout.setOnChildScrollUpListener(new OnChildScrollUpListener() {
  @Override
  public boolean canChildScrollUp() {
    return mListView.getFirstVisiblePosition() > 0 || 
           mListView.getChildAt(0) == null || 
           mListView.getChildAt(0).getTop() < 0;
  }
});
person goRGon    schedule 22.10.2014
comment
У меня отлично работает! Спасибо! - person Bastien Beurier; 11.01.2015
comment
И не забудьте изменить макет xml с android.support.v4.widget.SwipeRefreshLayout на yourpackage.GeneralSwipeRefreshLayout - person Jaky71; 19.03.2015
comment
Как бы вы поменяли его на RecyclerView? - person android developer; 28.02.2017

Просто создайте класс, который расширяет SwipeRefreshLayout и переопределяет метод canChildScrollUp(). Верните true, если вы хотите прокрутить вниз для своего элемента управления.

Например, для просмотра прокрутки вы можете попробовать это,

@override.
boolean canChildScrollUp()
{
   //your condition to check scrollview reached at top while scrolling
   if(scrollview.getScrollY() == 0.0)
      return true;
   else
      return false;
}
person AndyN    schedule 23.04.2014
comment
Спасибо за ваш ответ. Я решил это, изменив XML. Но все равно спасибо за поддержку. - person rupesh; 23.04.2014
comment
+1 за отличный ответ. У меня это работает, но о каком состоянии вы говорите? @ User22791 - person moDev; 23.04.2014
comment
Если я просто продолжу return true, то обновление не сработает для меня - person moDev; 23.04.2014
comment
@droid_dev да. Вы не можете сказать правду. Это всегда позволит вашему списку или прокрутке прокручивать вверх что угодно. Вы должны использовать какое-то условие, соответствующее вашим критериям - person AndyN; 23.04.2014
comment
@ rup35h что вы изменили в макете? опубликуйте свой ответ, чтобы другие тоже могли получить помощь. - person AndyN; 23.04.2014
comment
@ User22791 Я понял, но думаю, как получить доступ к списку в другом классе? Как он будет слушать время выполнения? - person moDev; 23.04.2014
comment
позвольте нам продолжить обсуждение в чате - person AndyN; 23.04.2014
comment
@droid_dev Попробуйте сделать свой список публичным статическим. - person AndyN; 23.04.2014
comment
if(scrollview.getScrollY() == 0.0) return true; else return false; - хм, а почему не return scrollview.getScrollY() == 0? - person Konrad Morawski; 01.02.2016

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

Имеет отношение к простой логике, которую SwipeRefreshLayout использует при проверке возможности прокрутки дочернего представления.

Я использовал ListView внутри ActionbarActivity и хотел включить пустое представление всякий раз, когда мой список был пуст. Это вызвало проблемы, поскольку у класса SwipeRefreshLayout может быть только один дочерний элемент. Обратите внимание, что он также проверяет способность этого дочернего элемента scrollUp, чтобы определить, вызывает ли раскрытие дочернего элемента scrollUp или вызывает обновление содержимого дочернего элемента.

Поэтому, если вы хотите использовать ту же логику, что и SwipeRefreshLayout, просто расширьте класс и создайте метод, позволяющий передавать дескриптор в ваше прокручиваемое представление. Обратите внимание, что стандартная реализация использует canScrollVertical (), который делает именно то, что мы хотим, но появляется только в SDK> = 14.

Также не забудьте включить конструктор, содержащий параметр «AttributeSet», когда вы расширяете класс, иначе у вас возникнут проблемы с использованием класса в ваших файлах макета.

Итак, в методе onCreate вашей Activity (в моем случае это был ActionBarActivity), который включает представление списка, просто вызовите setMyScrollableView, передавая ваш ListView или любое другое представление, которое вы используете для прокрутки.

/*Constructor*/
public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
}


 View mMyScrollableView = null;  //The content that get's pulled down.
 /*Method used to pass in my scrollable view*/
 public void setMyScrollableView(View view){
    mMyScrollableView = view;
 }

/**
 * @return Whether it is possible for the child view of this layout to
 * scroll up.  This was taken for the most part directly from SwipeRefreshLayout
 */
public boolean canChildScrollUp() {
    if(mMyScrollableView == null)
      return false;
    if (android.os.Build.VERSION.SDK_INT < 14) {
        if (mMyScrollableView instanceof AbsListView) {
            final AbsListView absListView = (AbsListView) mMyScrollableView;
            return absListView.getChildCount() > 0
        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
        .getTop() < absListView.getPaddingTop());
        } else {
            return mMyScrollableView.getScrollY() > 0;
        }
    } else {
        return ViewCompat.canScrollVertically(mMyScrollableView, -1);
    }
}
person guyland123    schedule 29.07.2014

Решение от @ User22791 работает отлично, и на основе этого я создал библиотеку, доступную на github, которую вы можете использовать (и изменять), чтобы упростить использование swipeRefreshLayout для разработчиков. Это здесь: https://github.com/dagova/referencedSwipeRefreshLayout.

По сути, вам просто нужно указать в макете вид, который нужно проверить в методе canChildScrollUp. Надеюсь, это будет полезно.

person David Varela    schedule 29.05.2014

Я также обнаружил, что другие ответы не совсем сработали.

Мне потребовалось некоторое время, чтобы понять, что они используют метод getScrollY(), который, как объясняется в этом ответе, является View метод, описывающий, как далеко он был прокручен в контейнере, а не метод, описывающий, насколько прокручен ваш контейнер Scroll.

Если вы используете тот же метод, что и в других ответах (переопределив метод canChildScrollUp()), вы можете легко проверить, находится ли Scrollable в самой высокой точке:

@Override
public boolean canChildScrollUp() {
    return !isListAtTop();
}

private boolean isListAtTop()   {   
    if(mGridView.getChildCount() == 0) return true;
    return mGridView.getChildAt(0).getTop() == 0;
}

(Как видите, я использую GridView, но вы тоже можете использовать ListView)

person Graeme    schedule 03.07.2014
comment
Такое решение содержит ошибку: mGridView.getChildAt (0) вернет первый видимый дочерний элемент, но не дочерний элемент в абсолютной нулевой позиции. Вот почему пользователь будет зависать каждый раз, когда первое видимое представление прокручивается до 0. - person goRGon; 23.10.2014

Более простое решение - использовать onScrollListener и проверить, видит ли пользователь firstElement.

someView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView absListView, int scrollState) {
        if (scrollState == SCROLL_STATE_IDLE) {
            if (isViewAtTop()) {
                swipeLayout.setEnabled(true);
            } else {
                swipeLayout.setEnabled(false);
            }
        }

    }

    @Override
    public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (firstVisibleItem == 0) {
            swipeLayout.setEnabled(true);
        } else {
            swipeLayout.setEnabled(false);
        }
    }
});

Где метод isViewAtTop () - это какой-то другой метод, который проверяет, что этот вид прокручивается вверх

person c0rp    schedule 25.07.2014

Хорошо, у меня это работает. Если SwipeRefreshLayout является корнем макета, а ScrollView находится глубоко в иерархии (я поместил ScrollView внутри RelativeLayout), а не прямой дочерний элемент SwipeRefreshLayout, он не сможет правильно определить смахивание по ScrollView.

Вы должны создать собственный класс, который расширяет SwipeRefreshLayout и переопределяет метод canChildScrollUp() в SwipRefreshLayout

Вот пример:

 public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {

    private ScrollView scrollview;

    public CustomSwipeRefreshLayout(Context context) {
        super(context);
    }

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

    public void setView(ScrollView view) {
        this.scrollview = view;
    }

    @Override
    public boolean canChildScrollUp() {
        return scrollview.getScrollY() != 0;
    }

}
person nitesh goel    schedule 15.05.2014