1 /* 2 * Copyright 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.swiperefreshlistfragment; 18 19 import android.content.Context; 20 import android.os.Bundle; 21 import android.support.v4.app.ListFragment; 22 import android.support.v4.view.ViewCompat; 23 import android.support.v4.widget.SwipeRefreshLayout; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.widget.ListView; 28 29 /** 30 * Subclass of {@link android.support.v4.app.ListFragment} which provides automatic support for 31 * providing the 'swipe-to-refresh' UX gesture by wrapping the the content view in a 32 * {@link android.support.v4.widget.SwipeRefreshLayout}. 33 */ 34 public class SwipeRefreshListFragment extends ListFragment { 35 36 private SwipeRefreshLayout mSwipeRefreshLayout; 37 38 @Override 39 public View onCreateView(LayoutInflater inflater, ViewGroup container, 40 Bundle savedInstanceState) { 41 42 // Create the list fragment's content view by calling the super method 43 final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState); 44 45 // Now create a SwipeRefreshLayout to wrap the fragment's content view 46 mSwipeRefreshLayout = new ListFragmentSwipeRefreshLayout(container.getContext()); 47 48 // Add the list fragment's content view to the SwipeRefreshLayout, making sure that it fills 49 // the SwipeRefreshLayout 50 mSwipeRefreshLayout.addView(listFragmentView, 51 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 52 53 // Make sure that the SwipeRefreshLayout will fill the fragment 54 mSwipeRefreshLayout.setLayoutParams( 55 new ViewGroup.LayoutParams( 56 ViewGroup.LayoutParams.MATCH_PARENT, 57 ViewGroup.LayoutParams.MATCH_PARENT)); 58 59 // Now return the SwipeRefreshLayout as this fragment's content view 60 return mSwipeRefreshLayout; 61 } 62 63 /** 64 * Set the {@link android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener} to listen for 65 * initiated refreshes. 66 * 67 * @see android.support.v4.widget.SwipeRefreshLayout#setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener) 68 */ 69 public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) { 70 mSwipeRefreshLayout.setOnRefreshListener(listener); 71 } 72 73 /** 74 * Returns whether the {@link android.support.v4.widget.SwipeRefreshLayout} is currently 75 * refreshing or not. 76 * 77 * @see android.support.v4.widget.SwipeRefreshLayout#isRefreshing() 78 */ 79 public boolean isRefreshing() { 80 return mSwipeRefreshLayout.isRefreshing(); 81 } 82 83 /** 84 * Set whether the {@link android.support.v4.widget.SwipeRefreshLayout} should be displaying 85 * that it is refreshing or not. 86 * 87 * @see android.support.v4.widget.SwipeRefreshLayout#setRefreshing(boolean) 88 */ 89 public void setRefreshing(boolean refreshing) { 90 mSwipeRefreshLayout.setRefreshing(refreshing); 91 } 92 93 /** 94 * Set the color scheme for the {@link android.support.v4.widget.SwipeRefreshLayout}. 95 * 96 * @see android.support.v4.widget.SwipeRefreshLayout#setColorScheme(int, int, int, int) 97 */ 98 public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) { 99 mSwipeRefreshLayout.setColorScheme(colorRes1, colorRes2, colorRes3, colorRes4); 100 } 101 102 /** 103 * @return the fragment's {@link android.support.v4.widget.SwipeRefreshLayout} widget. 104 */ 105 public SwipeRefreshLayout getSwipeRefreshLayout() { 106 return mSwipeRefreshLayout; 107 } 108 109 /** 110 * Sub-class of {@link android.support.v4.widget.SwipeRefreshLayout} for use in this 111 * {@link android.support.v4.app.ListFragment}. The reason that this is needed is because 112 * {@link android.support.v4.widget.SwipeRefreshLayout} only supports a single child, which it 113 * expects to be the one which triggers refreshes. In our case the layout's child is the content 114 * view returned from 115 * {@link android.support.v4.app.ListFragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)} 116 * which is a {@link android.view.ViewGroup}. 117 * 118 * <p>To enable 'swipe-to-refresh' support via the {@link android.widget.ListView} we need to 119 * override the default behavior and properly signal when a gesture is possible. This is done by 120 * overriding {@link #canChildScrollUp()}. 121 */ 122 private class ListFragmentSwipeRefreshLayout extends SwipeRefreshLayout { 123 124 public ListFragmentSwipeRefreshLayout(Context context) { 125 super(context); 126 } 127 128 /** 129 * As mentioned above, we need to override this method to properly signal when a 130 * 'swipe-to-refresh' is possible. 131 * 132 * @return true if the {@link android.widget.ListView} is visible and can scroll up. 133 */ 134 @Override 135 public boolean canChildScrollUp() { 136 final ListView listView = getListView(); 137 if (listView.getVisibility() == View.VISIBLE) { 138 return canListViewScrollUp(listView); 139 } else { 140 return false; 141 } 142 } 143 144 } 145 147 /** 148 * Utility method to check whether a {@link ListView} can scroll up from it's current position. 149 * Handles platform version differences, providing backwards compatible functionality where 150 * needed. 151 */ 152 private static boolean canListViewScrollUp(ListView listView) { 153 if (android.os.Build.VERSION.SDK_INT >= 14) { 154 // For ICS and above we can call canScrollVertically() to determine this 155 return ViewCompat.canScrollVertically(listView, -1); 156 } else { 157 // Pre-ICS we need to manually check the first visible item and the child view's top 158 // value 159 return listView.getChildCount() > 0 && 160 (listView.getFirstVisiblePosition() > 0 161 || listView.getChildAt(0).getTop() < listView.getPaddingTop()); 162 } 163 } 165 166 }