SwipeRefreshLayout setRefreshing () изначально не показывает индикатор


144

У меня есть очень простой макет , но когда я звоню setRefreshing(true)в onActivityCreated()моем фрагменте, он не показывает на начальном этапе.

Это показывает только когда я делаю тягу, чтобы обновить. Есть идеи, почему он не появляется изначально?

Фрагмент xml:

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

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

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

        </RelativeLayout>


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

Код фрагмента:

public static class LinkDetailsFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener {

    @InjectView(R.id.swipe_container)
    SwipeRefreshLayout mSwipeContainer;

    public static LinkDetailsFragment newInstance(String subreddit, String linkId) {
        Bundle args = new Bundle();
        args.putString(EXTRA_SUBREDDIT, subreddit);
        args.putString(EXTRA_LINK_ID, linkId);

        LinkDetailsFragment fragment = new LinkDetailsFragment();
        fragment.setArguments(args);

        return fragment;
    }

    public LinkDetailsFragment() {
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mSwipeContainer.setOnRefreshListener(this);
        mSwipeContainer.setColorScheme(android.R.color.holo_blue_bright,
                android.R.color.holo_green_light,
                android.R.color.holo_orange_light,
                android.R.color.holo_red_light);
        mSwipeContainer.setRefreshing(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View rootView = inflater.inflate(R.layout.fragment_link_details, container, false);
        ButterKnife.inject(this, rootView);
        return rootView;
    }

    @Override
    public void onRefresh() {
        // refresh
    }
}

Какую версию вы используете?
Ахмед Хегазы

скомпилировать "com.android.support:appcompat-v7:21.0.0"
thunderousNinja

Я подтверждаю, что эта проблема произошла со мной с этой версии на ходу. Более ранние версии не имеют никаких проблем с этим. Я выложу решение, если получу.
Ахмед Хегазы

Позвольте мне попробовать более раннюю версию
thunderousNinja

Также не работает на v20 для меня. На какой версии он работает для вас?
thunderousNinja

Ответы:


307

Столкнулся с той же проблемой. Мое решение -

mSwipeRefreshLayout.post(new Runnable() {
    @Override
    public void run() {
        mSwipeRefreshLayout.setRefreshing(true);
    }
});

1
Работает хорошо, но я не знаю, почему мы должны сделать это вместо mSwipeRefreshLayout.setRefreshing (true);
Cocorico


1
Это не хорошее решение / обходной путь. Если пользователь находится в режиме полета, ваш refreshLayout начнет обновление в своем собственном потоке после того, как уже запустит сетевой запрос и получит его ответ (в этом случае ошибка). При обработке, чтобы остановить refreshLayout, он не будет работать, так как он еще не начался! Другими словами, refreshLayout начнет обновляться после получения вашего ответа. Удачи вам остановить это
Самер

1
Как я помню "посты" синхронизируются и запускаются в порядке добавления. Таким образом, вы можете добавить еще один пост, чтобы остановить его.
Владимир Байдалка

2
Возможно, это выглядит проще всего в реализации, но это нехорошо. Решение @ niks.stack, представленное ниже , лучше, так как не требует никаких изменений в коде, использующем его, поэтому, когда в конечном итоге эта ошибка исправлена ​​в поддержке lib, вы просто переключаетесь обратно на поддержку lib SwipeRefreshLayout
Marcin Orlowski

100

См. Ответ Владимира Байдалки.

Это старые обходные пути.

Это используется для работы на более раннюю версию android.support.v4, но с версии 21.0.0 продолжающегося он не работает и до сих пор существует с android.support.v4:21.0.3выпущено на 10-12 декабре 2014 года , и это является причиной.

Индикатор SwipeRefreshLayout не отображается при setRefreshing(true)вызове доSwipeRefreshLayout.onMeasure()

Временное решение:

призывая setProgressViewOffset()на SwipeRefreshLayoutчто invalidtes вида окружности макета , вызывающий SwipeRefreshLayout.onMeasure()быть вызваны немедленно.

mSwipeRefreshLayout.setProgressViewOffset(false, 0,
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
mSwipeRefreshLayout.setRefreshing(true);

ОБНОВЛЕНИЕ Лучший обходной путь

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

TypedValue typed_value = new TypedValue();
getActivity().getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.actionBarSize, typed_value, true);
mSwipeRefreshLayout.setProgressViewOffset(false, 0, getResources().getDimensionPixelSize(typed_value.resourceId));

ОБНОВЛЕНИЕ 20 ноября 2014

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

Например.

handler.postDelayed(new Runnable() {

    @Override
    public void run() {
        mSwipeRefreshLayout.setRefreshing(true);
    }
}, 1000);

или как упоминался ответ Владимира Байдалки.

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


Вы много тестировали с delay time? Я использую 500на моем Galaxy S4. Не уверен, что это будет проблемой на другом устройстве.
theblang

Я не проводил много тестов с задержкой, но думаю, 500что все будет в порядке. Я проверил это на. emulatorЯ просто хотел быть safeс 1000миллисекундами
Ахмед Хегази

3
Там нет необходимости размещать отложено. Вы можете просто опубликовать. публикация без задержки просто означает «сделай это, как только закончишь с тем, что делаешь сейчас». и то, что он делает сейчас, измеряет и выкладывает ваш пользовательский интерфейс.
Орен

47

Мое решение состоит в том, чтобы переопределить SwipeRefreshLayout:

public class MySwipeRefreshLayout extends SwipeRefreshLayout {

    private boolean mMeasured = false;
    private boolean mPreMeasureRefreshing = false;

    public MySwipeRefreshLayout(final Context context) {
        super(context);
    }

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

    @Override
    public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!mMeasured) {
            mMeasured = true;
            setRefreshing(mPreMeasureRefreshing);
        }
    }

    @Override
    public void setRefreshing(final boolean refreshing) {
        if (mMeasured) {
            super.setRefreshing(refreshing);
        } else {
            mPreMeasureRefreshing = refreshing;
        }
    }
}

3
Преимущество вашего решения - сохранить нетронутым смещение вида! Таким образом, мы продолжаем в соответствии с шаблоном дизайна, указанным здесь: google.com/design/spec/patterns/… . Спасибо!
Игорь де Лоренци

Не могли бы вы объяснить, как это работает? Это работает, но я не совсем понимаю внутреннюю работу. Я просто скучаю по чему-то тривиальному?
Шри

setRefreshing работает только после вызова onMeasure, поэтому мы храним локальный флаг обновления, а при первом вызове onMeasure применяем его
nikita.zhelonkin

5
Мне нравится это решение, потому что оно означает, что код, вызывающий SwipeRefreshLayout, может быть именно таким, каким мы хотим, без каких-либо сложностей. В основном это исправило ошибку в SwipeRefreshLayout. SwipeRefreshLayout действительно должен быть реализован таким образом.
DataGraham

Этот ответ может показаться не таким простым, но он хорошо решает проблему с помощью многих версий библиотек поддержки (в моем случае это 23.1.1). Согласно заявке эта проблема не устранена @ 23.2. code.google.com/p/android/issues/detail?id=77712
Роберт

20
mRefreshLayout.getViewTreeObserver()
                .addOnGlobalLayoutListener(
                        new ViewTreeObserver.OnGlobalLayoutListener() {
                            @Override
                            public void onGlobalLayout() {
                                mRefreshLayout
                                        .getViewTreeObserver()
                                        .removeGlobalOnLayoutListener(this);
                                mRefreshLayout.setRefreshing(true);
                            }
                        });

3
Этот ответ является единственным, который не является взломом, поэтому его следует принять
Генрих

1
Просто небольшая вещь: .removeGlobalOnLayoutListener должен быть .removeOnGlobalLayoutListener
mkuech

1
@mkeuch зависит от того, на какой API вы нацеливаетесь. Если вы нацеливаетесь на API16, вы должны выполнить проверку версии API и использовать оба варианта.
Марко

Мне нравится этот ответ немного больше, чем ответ с наибольшим количеством голосов, потому что более понятно, почему он используется.
Марсель Бро

Я должен исправить себя, это решение не всегда работает для меня. В некоторых случаях setRefreshing(false)завершение вызова onGlobalLayoutListener()не отменяет индикатор загрузки.
Марсель Бро


4

Судя по ответу Владимира Байдалки , это всего лишь маленькая идея, которая поможет вам поддерживать чистоту кода. Я считаю, что это заслуживает поста: вы сможете легко изменить поведение от публикации до прямого вызова метода, как только ошибка будет исправлена.

Напишите служебный класс, например:

public class Utils
{
    private Utils()
    {
    }

    public static void setRefreshing(final SwipeRefreshLayout swipeRefreshLayout, final boolean isRefreshing)
    {
        // From Guava, or write your own checking code
        checkNonNullArg(swipeRefreshLayout);
        swipeRefreshLayout.post(new Runnable()
        {
            @Override
            public void run()
            {
                swipeRefreshLayout.setRefreshing(isRefreshing);
            }
        });
    }
}

В вашем коде замените mSwipeContainer.setRefreshing(isRefreshing)на Utils.setRefreshing(mSwipeContainer, isRefreshing): теперь только одна точка в коде должна быть изменена после исправления ошибки, Utilsкласс. Метод также может быть встроен (и удален из Utils).

Обычно не будет заметной визуальной разницы. Имейте в виду, что ожидающее обновление может сохранить ваши старые Activityэкземпляры, удерживая SwipeRefreshLayoutих в иерархии представлений. Если это проблема, настройте метод на использование WeakReferences, но обычно вы все равно не блокируете поток пользовательского интерфейса и, таким образом, задерживает gc только на пару миллисекунд.


2

Также вы можете вызвать этот метод перед setRefreshing ..

    swipeRefreshLayout.measure(View.MEASURED_SIZE_MASK,View.MEASURED_HEIGHT_STATE_SHIFT);
swipeRefreshLayout.setRefreshing(true);

Это работает для меня.


1

Я использовал библиотеку AppCompat com.android.support:appcompat-v7:21.0.3, используя тот же подход, и это сработало. Итак, вы обновляете версию этой библиотеки.

Совет: RelativeLayoutне поддерживает ориентацию, это атрибут для LinearLayout.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
                                                 Bundle savedInstanceState) {       
  ViewGroup view = (ViewGroup) inflater.inflate(R.layout.license_fragment, 
           container, false);ButterKnife.inject(this, view);
    // Setting up Pull to Refresh
    swipeToRefreshLayout.setOnRefreshListener(this);
    // Indicator colors for refresh
    swipeToRefreshLayout.setColorSchemeResources(R.color.green, 
                R.color.light_green);
}

Макет XML:

<android.support.v4.widget.SwipeRefreshLayout>

<ScrollView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:paddingBottom="@dimen/activity_margin_vertical"
                    android:paddingTop="@dimen/activity_margin_vertical">

    <!-- Content -->
</ScrollView>

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

1

Помимо Владимира Байдалки используйте также следующий код:

swipeContainer.post(new Runnable() {
        @Override
        public void run() {
            swipeContainer.setRefreshing(false);
        }
    });

Пояснение Я реализовал решение, данное Владимиром Байдалкой (с использованием фрагментов), но после запуска swipeReferesh оно никогда не исчезало даже при вызове, swipeContainer.setRefreshing(false); поэтому пришлось реализовать приведенный выше код, который решил мою проблему. любые идеи, почему это происходит, приветствуются.

С Уважением,

'Com.android.support:appcompat-v7:22.2.1'


1

@ niks.stack В ответ на его ответ я покажу колесо прогресса после того, как onLayout()оно будет сделано. Когда я использовал его сразу после onMeasure(), это не учитывало некоторые из смещений, но использование его после onLayout()сделало.

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    if (!mLaidOut) {
        mLaidOut = true;
        setRefreshing(mPreLayoutRefreshing);
    }
}

@Override
public void setRefreshing(boolean refreshing) {
    if (mLaidOut) {
        super.setRefreshing(refreshing);
    } else {
        mPreLayoutRefreshing = refreshing;
    }
}

Я искал хороший обратный звонок после onMeasure! Thnx. Меня раздражало, что на начальном этапе смещение было выключено ...
xdbas

0

Мое решение (без поддержки v7) -

TypedValue typed_value = new TypedValue();
getTheme().resolveAttribute(android.R.attr.actionBarSize, typed_value, true);
swipeLayout.setProgressViewOffset(false, 0, getResources().getDimensionPixelSize(typed_value.resourceId));

if(!swipeLayout.isEnabled())
     swipeLayout.setEnabled(true);
swipeLayout.setRefreshing(true);

0

Я использую 'com.android.support:appcompat-v7:23.1.1'

swipeRefreshLayout.post(new Runnable() {
        @Override
        public void run() {
            swipeRefreshLayout.setRefreshing(true);
            getData();
        }
    });

Ранее я использовал swipeRefreshLayout.setRefreshing(true);внутри getData()метода, поэтому он не работал. Я не знаю, почему это не работает внутри метода.

Хотя я использовал swipeRefreshLayout.setRefreshing(true);только один раз в моем фрагменте.



-1

Другим обходным решением является создание нового элемента управления и производного от SwipeRefreshLayout. Переопределите функцию OnMeasure и снова включите обновление, если обновление активировано. Я использую это решение в проекте xamarin, и оно работает хорошо. Вот пример кода C #:

class MySwipeRefreshLayout : SwipeRefreshLayout
{
    /// <summary>
    /// used to indentify, if measure was called for the first time
    /// </summary>
    private bool m_MeasureCalled;

    public MvxSwipeRefreshLayout(Context context, IAttributeSet attrs)
        : base(context, attrs)
    {
    }

    public MvxSwipeRefreshLayout(Context context)
        : base(context)
    {
    }

    public override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        base.OnMeasure(widthMeasureSpec, heightMeasureSpec);

        if (!m_MeasureCalled)
        {
            //change refreshing only one time
            m_MeasureCalled = true;

            if (Refreshing)
            {
                Refreshing = false;
                Refreshing = true;
            }
        }
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.