Поддержка Android v4 SwipeRefreshLayout проблема с пустым представлением

SwipeRefresh не работает после установки пустого представления для списка, которое является единственным дочерним элементом макета SwipeRefresh. Как решить эту проблему?


person Dinesh    schedule 09.04.2014    source источник
comment
Я нашел решение. Нам нужно установить android:clickable(true) для пустого представления, тогда все будет работать нормально.   -  person Dinesh    schedule 10.04.2014
comment
Можете ли вы опубликовать XML вашего макета? Я столкнулся с той же проблемой и не могу заставить ее работать с вашим решением.   -  person Distwo    schedule 16.05.2014
comment
Я добавил пустой вид, но я не могу смахнуть этот пустой вид, чтобы обновить его. Могу ли я получить решение для этого .. Заранее спасибо.   -  person Ashokchakravarthi Nagarajan    schedule 11.06.2014
comment
@Dinesh, вы должны опубликовать свое решение в качестве ответа и принять его - это обычная практика SO. В идеале, с примером xml.   -  person Alexander Malakhov    schedule 23.03.2015
comment
См. мое решение (stackoverflow.com/a/40466223/6608860). Оно должно работать и в этом случае.   -  person Gaurav Karwa    schedule 07.11.2016


Ответы (9)


Вот решение: вы можете просто использовать эту иерархию представлений:

    <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>

Затем в коде вы просто вызываете:

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

Вот и все.

person nitesh goel    schedule 15.05.2014
comment
Красиво, проголосовал. Но у меня есть еще одна проблема: я не могу смахнуть этот пустой вид, чтобы обновить его. не так ли .. Могу ли я получить решение для этого .. Заранее спасибо. @нитеш - person Ashokchakravarthi Nagarajan; 11.06.2014
comment
вам нужно переопределить метод canChildScrollUp(), который находится в классе SwipeRefreshLayout. Проверьте мой ответ здесь: «swiperefreshlayout в android»> stackoverflow.com/questions/23236650/ предназначен для просмотра прокрутки, но вы можете добавить некоторую логику для своего собственного представления - person nitesh goel; 11.06.2014
comment
Если вы собираетесь взять чужой ответ и использовать его как свой, по крайней мере, отдайте ему должное: stackoverflow.com/a/23424655/893130 - person n1te; 11.10.2015
comment
@niteshgoel, пожалуйста, ответьте и на это: stackoverflow.com/questions/36434071/ - person ; 05.04.2016
comment
@nitesh goel: я также столкнулся с той же проблемой, с которой столкнулся Ашок при использовании этого решения. - person Madhan; 17.11.2016

У меня тоже была эта проблема, и я решил ее без дополнительного кода в действии, используя приведенный ниже макет.

Если вы используете ListActivity или ListFragment, он обрабатывает отображение/скрытие пустого представления для вас, а обновление работает с пустым списком, а также с этой структурой макета.

Никаких хаков не надо, все в SwipeRefreshLayout, как и должно быть.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/Refresher"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:orientation="vertical"
        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"
            android:divider="@null" />
        <ScrollView
            android:id="@android:id/empty"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <TextView
                android:text="Geen formulieren gevonden"
                style="@style/text.EmptyView" />
        </ScrollView>
    </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>

Надеюсь, это поможет вам. Если это так, не забудьте принять ответ.

Примечание: это дубликат моего ответа на этот вопрос., но этот вопрос в значительной степени дублирует этот вопрос... :)

ОБНОВЛЕНИЕ
Если SwipeRefreshLayout активирован слишком рано, вы можете реализовать ListView.OnScroll с помощью:

_refresher.Enabled = e.FirstVisibleItem == 0;

Это отключает обновление до тех пор, пока вы не прокрутите страницу вверх. Это необходимо только при использовании ListView, новый RecyclerView работает как положено.

person avb    schedule 16.07.2015
comment
для меня это вызывает проблемы с прокруткой - person Malachiasz; 29.01.2016
comment
Какие именно проблемы? Вы реализовали шаблон просмотра? - person avb; 30.01.2016
comment
Да, я сделал. Проблема заключалась в том, что при прокрутке списка вниз свайп-обновление активировалось слишком рано. Описанный ниже CustomSwipeRefreshLayout работает корректно. - person Malachiasz; 01.02.2016

Проблема для меня заключалась в том, что когда было видно пустое представление, обновляющийся круг не отображался, хотя метод обновления работал. Для меня этот код сработал, надеюсь, поможет.

  • макет xml:

    <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>
    

  • пользовательский 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);
        }
    }
    
  • и в моем фрагменте, где я использую макет, я только устанавливаю пролистывание для обновления макета следующим образом:

    mSwipeRefreshLayout.setListView(mListView);
    
  • пустой вид ListView устанавливается следующим образом:

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

Я надеюсь, что это помогает.

person gipsey    schedule 02.09.2015

вот как я это сделал (вероятно, не очень элегантное решение)

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();
    }
}

пустой макет

<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" />

макет списка

    <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>
person orium    schedule 02.12.2014
comment
Мне это решение нравится больше, чем 2 SwipeRefreshLayouts, так как мне нужно позаботиться только об одном SRL. - person kotucz; 12.01.2015
comment
@orium, пожалуйста, ответьте и на это: поддержка виджета v4 swiperefreshlayout работает, но не отображается"> stackoverflow.com/questions/36434071/ - person ; 05.04.2016

Вы можете ознакомиться с этим вопросом. Я опубликовал обходное решение.

Android — SwipeRefreshLayout с пустым текстовым представлением

person hufeng03    schedule 12.04.2014

Я исправил эту проблему, используя два SwipeToRefreshLayouts.

  • Один для упаковки ListView
  • Другой как пустой вид

Я разместил свой код на GitHub.

person Tobrun    schedule 14.04.2014
comment
Это сработало, но мой ListView больше не доступен для кликов. - person Edward van Raak; 14.08.2014
comment
Я переместил SwipeToRefreshLayout для emptyView над SwipeToRefreshLayout для ListView, и теперь он работает. - person Edward van Raak; 14.08.2014

Как @Dinesh написал выше, я использовал clickable="true", как показано ниже, с пустым RecyclerView:

mRecyclerView.setVisibility(View.VISIBLE);
mRecyclerView.setClickable(true);
myText.setVisibility(View.VISIBLE);

xml макет:

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

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

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


    <TextView
        android:id="@+id/myText"
        android:visibility="gone"
        android:text="@string/noListItem"
        android:textSize="18sp"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

RecyclerView имеет рост match_parent, а SwipeRefresh имеет wrap_content. Когда в списке есть элемент, не забудьте сделать текст gone.

person Jemshit Iskenderov    schedule 06.08.2015

У меня есть еще одна идея, которая хорошо работает. Подкласс ListView и переопределение setAdapter() и setEmptyView(). setEmptyView должен просто хранить представление, которое будет использоваться для пустого представления, а setAdapter должен зарегистрировать DataSetObserver, который будет скрывать/показывать пустое представление без изменения видимость представления списка. Вероятно, вы захотите использовать FrameLayout, который содержит ваш SwipeToRefreshLayout и пустое представление (как одноуровневое). Затем вы можете легко расположить пустой вид сверху/посередине/снизу и т. д., используя гравитацию и гравитацию макета. Вы также можете использовать RelativeLayout, но я не пробовал. Вы захотите каким-то образом отменить регистрацию dataSetObserver, я думаю, вы можете сделать это в onDetachFromWindow, как я сделал ниже.

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);
        }
    }

}

Пример файла макета:

    <?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>

Хотя это требует немного больше кода, чем просто создание подкласса ListView и изменение setVisibility таким образом, чтобы он не устанавливал список в GONE/HIDDEN, я чувствую, что это меньше взлома и все же позволяет пользователю установить список в GONE/Hidden если они были нужны.

person Matt Wolfe    schedule 10.09.2015

Я пробовал взломать пользовательский интерфейс, но это не сработало, единственное сработавшее решение - установить адаптер. передать пустое значение, убедитесь, что значение не будет нулевым.

NotificationListAdapter notificationListAdapter;
notificationListAdapter = new NotificationListAdapter(mContext,notificationResponse);
reCycleViewNotificationList.setAdapter(notificationListAdapter); // weather ListView or RecyclerView
person Avinash Ajay Pandey    schedule 27.08.2019