Android - SwipeRefreshLayout with empty textview

2020-01-30 06:42发布

I've implemented SwipeRefreshLayout into my app but it can only hold one direct child which should be the listview. I'm trying to figure out how to add an empty textview to the following working XML file:

<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ListView
        android:id="@+id/listViewConversation"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:dividerHeight="1dp" />

</android.support.v4.widget.SwipeRefreshLayout>

Wrapping it in a Linear/Relative layout makes it buggy because the listview will always update when you want to slide back up the listview. One way I can think of is doing this programmatically but I guess that's not the best option.

You can learn how to implement it using this tutorial: Swipe to refresh GUIDE

So basically it all works fine but I would like to add an empty view that shows a message when the listview is empty.

15条回答
甜甜的少女心
2楼-- · 2020-01-30 07:11

Actually, the only think you are missing is having that empty TextView be wrapped with a scrollable container - for example ScrollView. For details, have a look at SwipeRefreshLayout.canChildScrollUp() method and its usage.

Anyway, back to the point. Here is a successful implementation:

activity_some.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:measureAllChildren="true">

        <WebView
            android:id="@+id/webview"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <include layout="@layout/empty" />

    </FrameLayout>

</android.support.v4.widget.SwipeRefreshLayout>

Where your empty.xml is basically anything you wish wrapped with a ScrollView.

empty.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/empty"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <TextView
        android:text="Nothing here..."
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</ScrollView>

Now in order to get rid of the famous SwipeRefreshLayout refresh-only-when-at-the-top issue, toggle the SwipeRefreshLayout when necessary (Fragment-specific):

private ViewTreeObserver.OnScrollChangedListener mOnScrollChangedListener;

@Override
public void onStart() {
    super.onStart();

    mOnScrollChangedListener = new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            int scrollY = mWebView.getScrollY();
            if (scrollY == 0)
                swipeLayout.setEnabled(true);
            else
                swipeLayout.setEnabled(false);

        }
    };
    swipeLayout.getViewTreeObserver().addOnScrollChangedListener(mOnScrollChangedListener);
}

@Override
public void onStop() {
    swipeLayout.getViewTreeObserver().removeOnScrollChangedListener(mOnScrollChangedListener);
    super.onStop();
}

That's it! Hope it helps! ;)

Btw, why would you use SwipeRefreshLayout with FrameLayout this way? Because this way you can do smooth transition animations, like crossfade effects, and any of your state views can be swipeable (in case you want a unified fetch/refresh/retry mechanism).

查看更多
Summer. ? 凉城
3楼-- · 2020-01-30 07:14

Based on some answers here and the source code of SwipeRefreshLayout, I have subclassed the view to specifically handle having a RecyclerView (or ListView) and also an "empty" view inside a container which is the child.

It expects a layout such as

<SwipeRefreshLayoutWithEmpty ...>
  <FrameLayout ...>
    <TextView android:text="List is Empty" ...>
    <RecyclerView ...>
  </FrameLayout>
</SwipeRefreshLayoutWithEmpty>

The code is:

public class SwipeRefreshLayoutWithEmpty extends SwipeRefreshLayout {
    private ViewGroup container;

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

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

    @Override
    public boolean canChildScrollUp() {
        // The swipe refresh layout has 2 children; the circle refresh indicator
        // and the view container. The container is needed here
        ViewGroup container = getContainer();
        if (container == null) {
            return false;
        }

        // The container has 2 children; the empty view and the scrollable view.
        // Use whichever one is visible and test that it can scroll
        View view = container.getChildAt(0);
        if (view.getVisibility() != View.VISIBLE) {
            view = container.getChildAt(1);
        }

        return ViewCompat.canScrollVertically(view, -1);
    }

    private ViewGroup getContainer() {
        // Cache this view
        if (container != null) {
            return container;
        }

        // The container may not be the first view. Need to iterate to find it
        for (int i=0; i<getChildCount(); i++) {
            if (getChildAt(i) instanceof ViewGroup) {
                container = (ViewGroup) getChildAt(i);

                if (container.getChildCount() != 2) {
                    throw new RuntimeException("Container must have an empty view and content view");
                }

                break;
            }
        }

        if (container == null) {
            throw new RuntimeException("Container view not found");
        }

        return container;
    }
}

Full gist: https://gist.github.com/grennis/16cb2b0c7f798418284dd2d754499b43

查看更多
一夜七次
4楼-- · 2020-01-30 07:14

Why not wrap everything inside SwipeRefreshLayout

<android.support.v4.widget.SwipeRefreshLayout 
    android:id="@+id/swipe"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center_horizontal"/>

    <RelativeLayout
        android:id="@android:id/empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        ...
    </RelativeLayout>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
查看更多
女痞
5楼-- · 2020-01-30 07:16

You may be just use NestedScrollView in SwipeRefreshLayout with single container. Below is the list of buggy use mRecyclerView.setNestedScrollingEnabled(false);

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <!-- some toolbar -->

    <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/conversationFragment_swipeRefresh"
            android:layout_alignParentBottom="true"
            android:layout_below="@+id/some_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <android.support.v4.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="match_parent">

            <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                <TextView
                        android:id="@+id/conversationFragment_noResultText"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:text="@string/FragmentConversations_empty"
                        android:layout_centerHorizontal="true" />

                <android.support.v7.widget.RecyclerView
                        android:id="@+id/conversationFragment_recyclerView"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent" />

            </FrameLayout>

        </android.support.v4.widget.NestedScrollView>

    </android.support.v4.widget.SwipeRefreshLayout>

</RelativeLayout>
查看更多
太酷不给撩
6楼-- · 2020-01-30 07:19

Here's what I did : I disabled the swipe to refresh, unless my listView is up.

mBookedProductsListView.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView absListView, int i) {
        }
        @Override
        public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount,     int totalItemCount) {
            if (firstVisibleItem == 0)
                mSwipeRefreshLayout.setEnabled(true);
            else
                mSwipeRefreshLayout.setEnabled(false);
        }
    });

My Xml :

<?xml version="1.0" encoding="utf-8"?>

<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipeRefreshLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

<RelativeLayout
        android:id="@+id/productListLL"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

    <EditText
            android:id="@+id/search"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_alignParentTop="true"
            android:background="#a4aeb8"
            android:drawableRight="@drawable/search_icon"
            android:paddingRight="15dp"
            android:textColor="@android:color/white"
            android:paddingLeft="15dp"
            android:hint="Rechercher"
            android:textColorHint="@android:color/white"
            android:inputType="textCapWords|textNoSuggestions"
            />

    <include android:layout_width="match_parent" android:layout_height="wrap_content" layout="@layout/filter_by_categories_buttons" android:id="@+id/categoriesTree" android:layout_below="@id/search" />

    <ListView
            android:id="@+id/productListLV"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="@drawable/product_listview_divider"
            android:dividerHeight="1dp"
            android:scrollbars="none"
            android:overScrollMode="never"
            android:choiceMode="multipleChoiceModal"
            android:background="#e7eaef"
            android:visibility="gone"
            android:layout_below="@id/categoriesTree"
            />

</RelativeLayout>

</android.support.v4.widget.SwipeRefreshLayout>

Works like a charm.

查看更多
Luminary・发光体
7楼-- · 2020-01-30 07:21

This worked for me

<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/lighter_white">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ListView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:background="@color/silver"
            android:layout_marginTop="10dp"
            android:divider="@android:color/transparent"
            android:dividerHeight="8dp"
            android:padding="4dp"/>

    </android.support.v4.widget.SwipeRefreshLayout>

    <TextView
        android:id="@+id/empty"
        android:text="@string/strNoRecordsFound"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textColor="@android:color/black"
        android:alpha="0.5"
        android:gravity="center">
    </TextView>
</FrameLayout>
查看更多
登录 后发表回答