Android中的SwiperefreshLayout

18

我在下面的布局中使用了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>

当我下拉我的SwipeRefreshLayout时,它是可以工作的,但是像上面的代码一样,我在里面放了一个滚动视图。所以当我向下拉我的滚动视图时,它会下降并且只有一半的图片显示出来。当我再次尝试上拉时,我的滚动视图不会上移,而是调用了SwipeRefreshLayout。我该怎么办?

请帮帮我。

7个回答

29

我认为更好的做法是使用带有监听器的扩展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;
  }
});

对我来说完美运作!谢谢! - Bastien Beurier
6
不要忘记将你的 XML 布局从 android.support.v4.widget.SwipeRefreshLayout 更改为 yourpackage.GeneralSwipeRefreshLayout - Jaky71
你会如何为RecyclerView进行修改? - android developer

6

只需创建一个类,继承 SwipeRefreshLayout覆盖方法 canChildScrollUp()。当你想向下滚动控件时返回true。

例如对于ScrollView,你可以尝试这样做:

@override.
boolean canChildScrollUp()
{
   //your condition to check scrollview reached at top while scrolling
   if(scrollview.getScrollY() == 0.0)
      return true;
   else
      return false;
}

如果我只保留“return true”,那么刷新对我没有作用。 - moDev
@droid_dev 是的。你不能只放一个 true。它会一直允许你的列表或滚动视图向上滚动。你必须使用某种与你的条件相匹配的条件。 - AndyN
@rup35h,你在布局中改了什么?也请发布你的答案,这样其他人也可以得到帮助。 - AndyN
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/51242/discussion-between-user22791-and-droid-dev - AndyN
如果(scrollview.getScrollY() == 0.0) 返回真; 否则 返回假; - 嗯,为什么不直接返回scrollview.getScrollY() == 0呢? - Konrad Morawski
显示剩余4条评论

5
正如其他人所说,如果您的可滚动视图(即listview)不是SwipeRefreshLayout的直接子项,则股票canChildScrollUp将无法工作。
这与SwipeRefreshLayout在检查子视图滚动能力时使用的简单逻辑有关。
我正在使用ActionbarActivity中的ListView,并希望在我的listview为空时包含一个空视图。这会导致问题,因为SwipeRefreshLayout类只能有一个子项。请注意,它还检查此子项的向上滚动能力,以确定下拉是否使子项向上滚动,或者是否使子项内容刷新。
因此,如果您想使用与SwipeRefreshLayout相同的逻辑,请扩展该类,并创建一个方法,允许您传递到您的可滚动视图的句柄。请注意,股票实现使用canScrollVertically(),它正是我们想要的,但仅出现在SDK> = 14中。
还要记得在扩展类时包含包含参数“AttributeSet”的构造函数,否则您将在布局文件中使用该类时遇到问题。
因此,在包含列表视图的Activity(在我的情况下是ActionBarActivity)的onCreate方法中,只需调用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);
    }
}

3
@User22791提供的解决方案完美有效,基于此,我创建了一个库,可以在github上使用(和修改),让开发者更容易使用swipeRefreshLayout。这里是链接:https://github.com/dagova/referencedSwipeRefreshLayout
基本上,您只需要在布局中引用要在canChildScrollUp方法中检查的视图即可。希望这对您有所帮助。

2
我还发现其他答案并不太可行。
我花了一段时间才想出他们在使用方法getScrollY(),正如这个回答所解释的那样,它是一个View方法,用于描述它在容器中滚动了多远,而不是描述你的滚动容器已经滚动了多少。
如果您使用其他答案中相同的技术(覆盖canChildScrollUp()方法),您可以轻松检查可滚动区域是否处于最高点:
@Override
public boolean canChildScrollUp() {
    return !isListAtTop();
}

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

正如您所看到的,我正在使用GridView,但您也可以使用ListView


1
这个解决方案存在一个错误:mGridView.getChildAt(0)将返回第一个可见的子项,而不是绝对位置为0的子项。这就是为什么每次第一个可见视图滚动到0时,用户都会被卡住。 - goRGon

2
更简单的解决方案是使用 onScrollListener 并检查用户是否能够看到第一个元素。
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()方法是另一个方法,用于检查此视图是否滚动到顶部。


1

好的,我已经让它工作了。如果SwipeRefreshLayout是布局的根,并且ScrollView深入到层次结构中(我把ScrollView放在了RelativeLayout中),而不是直接是SwipeRefreshLayout的子元素,则它无法正确检测ScrollView的上滑操作。

您应该创建一个自定义类,该类扩展SwipeRefreshLayout并覆盖SwipRefreshLayout中的canChildScrollUp()方法。

这里是一个例子:

 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;
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接