SwipeRefreshListFragment.java

  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
 17package eu.siacs.conversations.ui.widget;
 18
 19import android.content.Context;
 20import android.os.Bundle;
 21import android.view.LayoutInflater;
 22import android.view.View;
 23import android.view.ViewGroup;
 24import android.widget.ListView;
 25
 26import androidx.fragment.app.ListFragment;
 27import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 28
 29import eu.siacs.conversations.R;
 30import eu.siacs.conversations.ui.util.StyledAttributes;
 31
 32/**
 33 * Subclass of {@link androidx.fragment.app.ListFragment} which provides automatic support for
 34 * providing the 'swipe-to-refresh' UX gesture by wrapping the the content view in a
 35 * {@link androidx.swiperefreshlayout.widget.SwipeRefreshLayout}.
 36 */
 37public class SwipeRefreshListFragment extends ListFragment {
 38
 39    private boolean enabled = false;
 40    private boolean refreshing = false;
 41
 42    private SwipeRefreshLayout.OnRefreshListener onRefreshListener;
 43
 44    private SwipeRefreshLayout mSwipeRefreshLayout;
 45
 46    @Override
 47    public View onCreateView(LayoutInflater inflater, ViewGroup container,
 48                             Bundle savedInstanceState) {
 49
 50        // Create the list fragment's content view by calling the super method
 51        final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState);
 52
 53        // Now create a SwipeRefreshLayout to wrap the fragment's content view
 54        mSwipeRefreshLayout = new ListFragmentSwipeRefreshLayout(container.getContext());
 55        mSwipeRefreshLayout.setEnabled(enabled);
 56        mSwipeRefreshLayout.setRefreshing(refreshing);
 57
 58        final Context context = getActivity();
 59        if (context != null) {
 60            mSwipeRefreshLayout.setColorSchemeColors(StyledAttributes.getColor(context, R.attr.colorAccent));
 61        }
 62
 63        if (onRefreshListener != null) {
 64            mSwipeRefreshLayout.setOnRefreshListener(onRefreshListener);
 65        }
 66
 67        // Add the list fragment's content view to the SwipeRefreshLayout, making sure that it fills
 68        // the SwipeRefreshLayout
 69        mSwipeRefreshLayout.addView(listFragmentView,
 70                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
 71
 72        // Make sure that the SwipeRefreshLayout will fill the fragment
 73        mSwipeRefreshLayout.setLayoutParams(
 74                new ViewGroup.LayoutParams(
 75                        ViewGroup.LayoutParams.MATCH_PARENT,
 76                        ViewGroup.LayoutParams.MATCH_PARENT));
 77
 78        // Now return the SwipeRefreshLayout as this fragment's content view
 79        return mSwipeRefreshLayout;
 80    }
 81
 82    /**
 83     * Set the {@link androidx.core.widget.SwipeRefreshLayout.OnRefreshListener} to listen for
 84     * initiated refreshes.
 85     *
 86     * @see androidx.core.widget.SwipeRefreshLayout#setOnRefreshListener(androidx.core.widget.SwipeRefreshLayout.OnRefreshListener)
 87     */
 88    public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) {
 89        onRefreshListener = listener;
 90        enabled = true;
 91        if (mSwipeRefreshLayout != null) {
 92            mSwipeRefreshLayout.setEnabled(true);
 93            mSwipeRefreshLayout.setOnRefreshListener(listener);
 94        }
 95    }
 96
 97    /**
 98     * Set whether the {@link androidx.core.widget.SwipeRefreshLayout} should be displaying
 99     * that it is refreshing or not.
100     *
101     * @see androidx.core.widget.SwipeRefreshLayout#setRefreshing(boolean)
102     */
103    public void setRefreshing(boolean refreshing) {
104        this.refreshing = refreshing;
105        if (mSwipeRefreshLayout != null) {
106            mSwipeRefreshLayout.setRefreshing(refreshing);
107        }
108    }
109
110
111    /**
112     * Sub-class of {@link androidx.core.widget.SwipeRefreshLayout} for use in this
113     * {@link androidx.core.app.ListFragment}. The reason that this is needed is because
114     * {@link androidx.core.widget.SwipeRefreshLayout} only supports a single child, which it
115     * expects to be the one which triggers refreshes. In our case the layout's child is the content
116     * view returned from
117     * {@link androidx.core.app.ListFragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)}
118     * which is a {@link android.view.ViewGroup}.
119     *
120     * <p>To enable 'swipe-to-refresh' support via the {@link android.widget.ListView} we need to
121     * override the default behavior and properly signal when a gesture is possible. This is done by
122     * overriding {@link #canChildScrollUp()}.
123     */
124    private class ListFragmentSwipeRefreshLayout extends SwipeRefreshLayout {
125
126        public ListFragmentSwipeRefreshLayout(Context context) {
127            super(context);
128        }
129
130        /**
131         * As mentioned above, we need to override this method to properly signal when a
132         * 'swipe-to-refresh' is possible.
133         *
134         * @return true if the {@link android.widget.ListView} is visible and can scroll up.
135         */
136        @Override
137        public boolean canChildScrollUp() {
138            final ListView listView = getListView();
139            if (listView.getVisibility() == View.VISIBLE) {
140                return listView.canScrollVertically(-1);
141            } else {
142                return false;
143            }
144        }
145
146    }
147
148}