I have a layout which includes a SwipeRefreshLayout, a ScrollView and a ViewPager.
The SwipeRefreshLayout is used to refresh the ViewPager and the ScrollView is because the ViewPager can have content that don't fits on display.
When I try to scroll down nothing happens but I can see there is more content to show. When I scroll up the SwipeRefresh is invoked.
My XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="${relativePackage}.${activityClass}" >
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
style="@style/TabLayout"
android:paddingEnd="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingStart="16dp" />
<com.gmail.bathingrad.school.widget.SwipeRefreshView
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.gmail.bathingrad.school.widget.SwipeRefreshView>
</LinearLayout>
Screenshot of how it look like (the last purple box is outside of display):
http://i.stack.imgur.com/hlrdz.png
Edit:
After some research I have found out that the problem is because of the fact that the ScrollView don't know the height of the largest item in the ViewPager. I'm searching for a way to change the height of the ViewPager dynamically.
After some thinking, writing and testing I have found a solution that works.
I put a extended ScrollView in every page of the ViewPager and then in the ScrollView I added a constructor with Context and SwipeRefreshLayout.
I overridden the method onScrollChanged and checked if the ScrollView was scrolled to the top. If it was not, I disabled the SwipeRefreshLayout.
<com.gmail.bathingrad.school.widget.SwipeRefreshView
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</android.support.v4.view.ViewPager>
</com.gmail.bathingrad.school.widget.SwipeRefreshView>
ScrollView.class:
public class ScrollView extends android.widget.ScrollView {
SwipeRefreshLayout swipeRefresh;
public ScrollView(Context context, SwipeRefreshLayout swipeRefresh) {
super(context);
this.swipeRefresh = swipeRefresh;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
Log.i("School", "T: " + t);
if (t != 0) {
swipeRefresh.setEnabled(false);
} else if (t == 0) {
swipeRefresh.setEnabled(true);
}
}
}
ViewPagerAdapter instantiateItem method:
@Override
public Object instantiateItem(ViewGroup container, int position) {
LinearLayout[] schemeLayout = readScheme.generateScheme();
ScrollView[] scrollView = new ScrollView[5];
// SwipeRefreshLayout
SwipeRefreshLayout swipeRefresh = (SwipeRefreshLayout) container.getParent();
swipeRefresh.setEnabled(true);
scrollView[position] = new ScrollView(context, swipeRefresh);
scrollView[position].addView(schemeLayout[position]);
container.addView(scrollView[position]);
return scrollView[position];
}
This works!
The reason why I call swipeRefresh.setEnabled(true);
is because of the problem that when the user scrolls down and then swipes to another page the swipeRefresh was disabled.
Hope someone will find this usefull
Google resolve this issue with an example posted on the google-sample github projet.
Create a view that extends the original SwipeRefreshLayout :
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.swiperefreshmultipleviews;
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
/**
* A descendant of {@link android.support.v4.widget.SwipeRefreshLayout} which supports multiple
* child views triggering a refresh gesture. You set the views which can trigger the gesture via
* {@link #setSwipeableChildren(int...)}, providing it the child ids.
*/
public class MultiSwipeRefreshLayout extends SwipeRefreshLayout {
private View[] mSwipeableChildren;
public MultiSwipeRefreshLayout(Context context) {
super(context);
}
public MultiSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Set the children which can trigger a refresh by swiping down when they are visible. These
* views need to be a descendant of this view.
*/
public void setSwipeableChildren(final int... ids) {
assert ids != null;
// Iterate through the ids and find the Views
mSwipeableChildren = new View[ids.length];
for (int i = 0; i < ids.length; i++) {
mSwipeableChildren[i] = findViewById(ids[i]);
}
}
// BEGIN_INCLUDE(can_child_scroll_up)
/**
* This method controls when the swipe-to-refresh gesture is triggered. By returning false here
* we are signifying that the view is in a state where a refresh gesture can start.
*
* <p>As {@link android.support.v4.widget.SwipeRefreshLayout} only supports one direct child by
* default, we need to manually iterate through our swipeable children to see if any are in a
* state to trigger the gesture. If so we return false to start the gesture.
*/
@Override
public boolean canChildScrollUp() {
if (mSwipeableChildren != null && mSwipeableChildren.length > 0) {
// Iterate through the scrollable children and check if any of them can not scroll up
for (View view : mSwipeableChildren) {
if (view != null && view.isShown() && !canViewScrollUp(view)) {
// If the view is shown, and can not scroll upwards, return false and start the
// gesture.
return false;
}
}
}
return true;
}
// END_INCLUDE(can_child_scroll_up)
// BEGIN_INCLUDE(can_view_scroll_up)
/**
* Utility method to check whether a {@link View} can scroll up from it's current position.
* Handles platform version differences, providing backwards compatible functionality where
* needed.
*/
private static boolean canViewScrollUp(View view) {
if (android.os.Build.VERSION.SDK_INT >= 14) {
// For ICS and above we can call canScrollVertically() to determine this
return ViewCompat.canScrollVertically(view, -1);
} else {
if (view instanceof AbsListView) {
// Pre-ICS we need to manually check the first visible item and the child view's top
// value
final AbsListView listView = (AbsListView) view;
return listView.getChildCount() > 0 &&
(listView.getFirstVisiblePosition() > 0
|| listView.getChildAt(0).getTop() < listView.getPaddingTop());
} else {
// For all other view types we just check the getScrollY() value
return view.getScrollY() > 0;
}
}
}
// END_INCLUDE(can_view_scroll_up)
}
Use this view in your xml layout :
<com.example.android.swiperefreshmultipleviews.MultiSwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swiperefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- your code -->
</com.example.android.swiperefreshmultipleviews.MultiSwipeRefreshLayout>
And in your activity or fragment, you can use this component with :
mSwipeRefreshLayout = (MultiSwipeRefreshLayout) view.findViewById(R.id.swiperefresh);
mSwipeRefreshLayout.setSwipeableChildren(android.R.id.list, android.R.id.empty);
android.R.id.list and android.R.id.empty is the ids for your list or recycler view. The view work perfectly with two list with same ids.
You can see real example at the google-sample github project.