5

I am using StickyHeaderListview in my project to display contents and for refreshing the list, I am using SwipeRefreshLayout. The problem here is, when I try to scroll up the list, it starts refreshing the list and not allowing to view the previous items of list.

I want the behavior should be such as the list get refresh only when I've reached to the first item and I try to scroll up , not everytime when i scroll up the list.

Can anyone help on this?

P.s. For implementing SwipeRefreshLayout, I am refering this example

AndyN
  • 1,742
  • 1
  • 15
  • 30
  • You are probably adding more than one View to your SwipeRefreshLayout. The following is stated in the reference regarding SwipeRefreshLayout: This layout should be made the parent of the view that will be refreshed as a result of the gesture and can only support one direct child. – Tobrun Apr 14 '14 at 21:45
  • @user1281750 I've added a `FrameLayout` inside the `SwipeRefreshLayout` which contains the `StickyHeadersList` and an empty view. – AndyN Apr 15 '14 at 04:22

5 Answers5

18

I faced the same problem when using StickyHeaderListview as a direct child of SwipeRefreshLayout. StickyHeaderListview is in fact a FrameLayout wrapping a ListView inside. As nitesh goel explained, this would lead to problems with canChildScrollUp(). Based on nitesh goel's example, this is a full version of CustomSwipeRefreshLayout that works well for me:

public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {

    /**
     * A StickyListHeadersListView whose parent view is this SwipeRefreshLayout
     */
    private StickyListHeadersListView mStickyListHeadersListView;

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

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

    public void setStickyListHeadersListView(StickyListHeadersListView stickyListHeadersListView) {
        mStickyListHeadersListView = stickyListHeadersListView;
    }

    @Override
    public boolean canChildScrollUp() {
        if (mStickyListHeadersListView != null) {
            // In order to scroll a StickyListHeadersListView up:
            // Firstly, the wrapped ListView must have at least one item
            return (mStickyListHeadersListView.getListChildCount() > 0) &&
                    // And then, the first visible item must not be the first item
                    ((mStickyListHeadersListView.getFirstVisiblePosition() > 0) ||
                            // If the first visible item is the first item,
                            // (we've reached the first item)
                            // make sure that its top must not cross over the padding top of the wrapped ListView
                            (mStickyListHeadersListView.getListChildAt(0).getTop() < 0));

            // If the wrapped ListView is empty or,
            // the first item is located below the padding top of the wrapped ListView,
            // we can allow performing refreshing now
        } else {
            // Fall back to default implementation
            return super.canChildScrollUp();
        }
    }
}
Thuy Trinh
  • 2,914
  • 1
  • 22
  • 35
  • 1
    Thank you very much for this, I wanted to mention to future googlers that this also works if you are using any kind of AdapterView child as your custom list view etc. – Daniel Wilson Sep 09 '14 at 09:07
3

Ok I have got it working. If the SwipeRefreshLayout is the root of the layout and the ListView resides deep into the hierarchy (I had put the ListView inside a RelativeLayout along with the empty TextView) and not the direct child of the SwipeRefreshLayout, it won’t detect a swipe up on the list view properly.

You should create a custom class that extends SwipeRefreshLayout and override canChildScrollUp() method in SwipRefreshLayout

Here is a example :

public class CustomSwipeRefreshLayout extends SwipeRefreshLayout{
    private AbsListView view;

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

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

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

    @Override
    public boolean canChildScrollUp() {
        return view.getFirstVisiblePosition()!=0;
    }
}
nitesh goel
  • 6,338
  • 2
  • 29
  • 38
2

I have had a similar problem, the direct child should be an instance of ScrollView (or ListView). The SwipeRefreshLayout will only take in account the direct child's scroll and not the child's of that direct child. I managed to solve this by using two SwipeRefreshLayouts.

I posted the code on github.

Tobrun
  • 18,291
  • 10
  • 66
  • 81
1

Hi i think i made something for a generally use :

public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
    private View v;

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

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

    public void setView(View v) {
        this.v = v;
    }

    @Override
    public boolean canChildScrollUp() {
        return this.v.canScrollVertically(-1);
    }
}

With that solution, only set the view you want to scroll inside the SwipeRefreshLayout, after call canChildScrollUp(). like this :

this.refreshLayout.setView(aView);
this.refreshLayout.canChildScrollUp();

I don't test it a lot, but if i'm right it will work for every view at every place (direct child or not) in the SwipeRefreshLayout.

(for me it was SwipeRefreshLayout => RelativeLayout => SrcollView => linearLayout)

Lucsartes
  • 321
  • 2
  • 13
0

This is very simple solution:

list.setOnScrollListener(new AbsListView.OnScrollListener() {                                                   
    @Override                                                                                                   
    public void onScrollStateChanged(AbsListView view, int scrollState) {                                       
    }                                                                                                           

    @Override                                                                                                   
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {    
        int topRowVerticalPosition = (list == null || list.getChildCount() == 0) ?                              
                0 : list.getChildAt(0).getTop();                                                                
        swipeRefreshLayout.setEnabled((topRowVerticalPosition >= 0));                                           
    }                                                                                                           
});                                                                                                             

So, if you're on the top of the listview you will be enabled to do refresh.

MilanNz
  • 1,323
  • 3
  • 12
  • 29