Android support v4 SwipeRefreshLayout empty view issue

后端 未结 9 1136
耶瑟儿~
耶瑟儿~ 2021-01-01 18:01

SwipeRefresh is not working after setting an empty view for listview which is the only child of a SwipeRefresh layout. How to solve this issue?

相关标签:
9条回答
  • 2021-01-01 18:35

    here's how i did this (probably not very elegant solution)

    private void createEmptyView() {
        ViewGroup parent = (ViewGroup) listView.getParent();
        emptyView = (TextView) getLayoutInflater(null)
                .inflate(R.layout.view_empty, parent, false);
        parent.addView(emptyView);
        listView.setEmptyView(emptyView);
    }
    
    public class FrameSwipeRefreshLayout extends SwipeRefreshLayout {
    
        private ViewGroup listView;
        public void setListView(ViewGroup list) {
            listView = list;
        }
    
        @Override
        public boolean canChildScrollUp() {
            if (listView != null) {
                View child = listView.getChildAt(0);
                return child != null && child.getTop() != 0;
            }
            return super.canChildScrollUp();
        }
    }
    

    empty layout

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:gravity="center_horizontal"
          android:clickable="true" />
    

    list layout

        <FrameSwipeRefreshLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                >
            <ListView
                android:id="@android:id/list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                />
            </FrameLayout>
        </FrameSwipeRefreshLayout>
    
    0 讨论(0)
  • 2021-01-01 18:35

    I have another idea which works well. Subclass ListView and override setAdapter() and setEmptyView(). setEmptyView should just store the view to use for the empty view, and setAdapter should register a DataSetObserver that will hide/show the empty view without altering the list view's visibility. You would probably want to use a FrameLayout that holds your SwipeToRefreshLayout and an empty view (as siblings). You can then position the empty view at the top/middle/bottom etc pretty easily using gravity and layout gravity. You could also use a RelativeLayout but I haven't tried. You will want to somehow unregister the dataSetObserver, I believe you may want to do that in onDetachFromWindow as I did below.

    public class SwipeToRefreshCompatibleList extends ListView {
    
        private boolean mIsEmpty = false;
        private View mEmptyView;
        private Adapter mAdapter;
    
        private DataSetObserver mLocalObserver = new  DataSetObserver() {
            @Override
            public void onChanged() {
                boolean isEmpty = mAdapter == null || mAdapter.getCount() == 0;
                if (mEmptyView != null) {
                    if (isEmpty != mIsEmpty) {
                        mEmptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
                    }
                }
                mIsEmpty = isEmpty;
            }
        };
    
        public SwipeToRefreshCompatibleList(Context context) {
            super(context);
        }
    
        public SwipeToRefreshCompatibleList(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public SwipeToRefreshCompatibleList(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public SwipeToRefreshCompatibleList(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
        @Override
        public void setAdapter(ListAdapter adapter) {
            mAdapter = adapter;
            adapter.registerDataSetObserver(mLocalObserver);
            super.setAdapter(adapter);
        }
    
    
        @Override
        public void setEmptyView(View view) {
            mEmptyView = view;
        }
    
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            if (mAdapter != null) {
                mAdapter.unregisterDataSetObserver(mLocalObserver);
            }
        }
    
    }
    

    Example layout file:

        <?xml version="1.0" encoding="utf-8"?>
        <FrameLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >
            <android.support.v4.widget.SwipeRefreshLayout
                android:id="@+id/swipe_container"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                >
    
                <com.company.widget.SwipeToRefreshCompatibleList
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/BasicList"
                    android:divider="@null"
                    />
    
    
            </android.support.v4.widget.SwipeRefreshLayout>
    
            <TextView
                android:padding="20dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_horizontal"
                android:layout_gravity="center"
                android:textSize="18sp"
                android:id="@android:id/empty"
                />
    
        </FrameLayout>
    

    While this takes a bit more code than simply subclassing ListView and altering setVisibility such that it won't set the list to GONE/HIDDEN, I feel like this is less of a hack and still allows the user to set the list to GONE/Hidden if they needed.

    0 讨论(0)
  • 2021-01-01 18:42

    Here is the solution: You can simply use this view hierarchy :

        <FrameLayout ...>
    
            <android.support.v4.widget.SwipeRefreshLayout ...>
    
                <ListView
                    android:id="@android:id/list" ... />
            </android.support.v4.widget.SwipeRefreshLayout>
    
            <TextView
                android:id="@android:id/empty" ...
                android:text="@string/empty_list"/>
        </FrameLayout>
    

    Then, in code, you just call:

    _listView.setEmptyView(findViewById(android.R.id.empty));
    

    That's it.

    0 讨论(0)
  • 2021-01-01 18:43

    The problem for me was that when the empty view was visible then the refreshing circle wasn't shown although the refreshing method worked. For me this code worked, I hope it helps.

    • the xml layout:

      <FrameLayout
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:clickable="true">
      
          <ListView
              android:id="@+id/listView"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:fadingEdge="none"
              android:footerDividersEnabled="false"
              android:headerDividersEnabled="false"/>
      
          <ScrollView
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:gravity="center">
      
              <TextView
                  android:id="@+id/empty_view"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:layout_gravity="center"
                  android:gravity="center"
                  android:visibility="gone"/>
          </ScrollView>
      </FrameLayout>
      

    • the custom SwipeRefreshLayout:

      package misc;
      
      import android.content.Context;
      import android.support.v4.widget.SwipeRefreshLayout;
      import android.widget.ListView;
      
      public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
          private ListView mListView;
      
          public CustomSwipeRefreshLayout(Context context) {
              super(context);
          }
      
          public void setListView(ListView listView) {
              mListView = listView;
          }
      
          @Override
          public boolean canChildScrollUp() {
              if (mListView == null) {
                  return true;
              }
      
              return mListView.canScrollVertically(-1);
          }
      }
      
    • and in my Fragment where I use the layout I only set the swipe to refresh layout in this way:

      mSwipeRefreshLayout.setListView(mListView);
      
    • the ListView's empty view is set in this way:

      TextView emptyView = (TextView) view.findViewById(R.id.empty_view);
      emptyView.setText("No data");
      mListView.setEmptyView(emptyView);
      

    I hope it helps.

    0 讨论(0)
  • 2021-01-01 18:47

    You can check out this issue. I posted a work around solution.

    Android - SwipeRefreshLayout with empty textview

    0 讨论(0)
  • 2021-01-01 18:52

    I have tried UI hack but it didn't work, the only solution worked is set adapter. pass empty value make sure the value will not be null.

    NotificationListAdapter notificationListAdapter;
    notificationListAdapter = new NotificationListAdapter(mContext,notificationResponse);
    reCycleViewNotificationList.setAdapter(notificationListAdapter); // weather ListView or RecyclerView
    
    0 讨论(0)
提交回复
热议问题