SwipeRefreshLayout setRefreshing () zeigt anfangs keine Anzeige an


144

Ich habe ein sehr einfaches Layout , aber wenn ich rufe setRefreshing(true)in onActivityCreated()meinem Fragmente, es zeigt nicht , zunächst.

Es wird nur angezeigt, wenn ich zum Aktualisieren ziehe. Irgendwelche Ideen, warum es anfangs nicht auftaucht?

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

Fragmentcode:

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

Welche Version benutzt du?
Ahmed Hegazy

kompilieren "com.android.support:appcompat-v7:21.0.0"
thunderousNinja

Ich bestätige, dass dieses Problem bei mir ab dieser Version aufgetreten ist. Frühere Versionen haben damit keine Probleme. Ich werde die Lösung veröffentlichen, wenn ich welche bekomme.
Ahmed Hegazy

Lassen Sie mich eine frühere Version versuchen
donnernde

Ich arbeite auch nicht an v20 für mich. An welcher Version arbeitet es für Sie?
donnernde

Antworten:


307

Konfrontiert mit dem gleichen Problem. Meine Lösung -

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

1
Funktioniert gut, aber ich weiß nicht, warum wir dies anstelle von mSwipeRefreshLayout.setRefreshing (true) tun müssen.
Cocorico


1
Dies ist keine gute Lösung / Problemumgehung. Wenn sich der Benutzer im Flugzeugmodus befindet, wird Ihr refreshLayout in einem eigenen Thread aktualisiert, nachdem die Netzwerkanforderung bereits gestartet und die Antwort empfangen wurde (in diesem Fall fehlgeschlagen). Wenn Sie damit umgehen, um das refreshLayout zu stoppen, würde es nicht funktionieren, da es noch nicht gestartet wurde! Mit anderen Worten, das refreshLayout wird aktualisiert, nachdem Sie Ihre Antwort erhalten haben. Viel Glück beim Stoppen
Samer

1
Wie ich mich erinnere, sind "Beiträge" synchronisiert und werden in der Reihenfolge des Hinzufügens ausgeführt. Sie können also einen weiteren Beitrag hinzufügen, um ihn zu stoppen.
Volodymyr Baydalka

2
Es sieht vielleicht am einfachsten in der Implementierung aus, ist aber nicht schön. Die unten stehende @ niks.stack-Lösung ist besser, da keine Änderungen am Code erforderlich sind. Wenn dieser Fehler in der Support-Bibliothek behoben ist, wechseln Sie einfach zurück zur Support-Bibliothek. SwipeRefreshLayout
Marcin Orlowski

100

Siehe stattdessen die Antwort von Volodymyr Baydalka.

Dies sind die alten Problemumgehungen.

Das funktionierte früher bei früheren Versionen von android.support.v4, aber ab Version 21.0.0 funktioniert es nicht mehr und existiert immer noch mit der android.support.v4:21.0.3Veröffentlichung vom 10. bis 12. Dezember 2014, und dies ist der Grund.

Der SwipeRefreshLayout-Indikator wird nicht angezeigt, wenn der setRefreshing(true)vor dem aufgerufen wirdSwipeRefreshLayout.onMeasure()

Problemumgehung:

Aufrufen setProgressViewOffset()des SwipeRefreshLayout, der die Kreisansicht des Layouts ungültig macht und SwipeRefreshLayout.onMeasure()sofort aufgerufen wird.

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

UPDATE Bessere Problemumgehung

Weil die Aktionsleiste dünner werden könnte, wenn sich die Ausrichtung ändert oder Sie die Größe der Aktionsleiste manuell festgelegt haben. Wir legen den Versatz in Pixel von oben in dieser Ansicht fest, bei dem der Fortschrittsspinner nach einer erfolgreichen Wischgeste auf die aktuelle Größe der Aktionsleiste zurückgesetzt werden soll.

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

UPDATE 20. November 2014

Wenn es für Ihre App nicht sehr wichtig ist, das SwipeRefreshLayout nach dem Start der Ansicht anzuzeigen. Sie können es einfach zu einem späteren Zeitpunkt veröffentlichen, indem Sie Handler oder andere Elemente verwenden.

als Beispiel.

handler.postDelayed(new Runnable() {

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

oder wie Volodymyr Baydalkas Antwort erwähnt.

Hier ist das Problem im Android Issue Tracker. Bitte stimmen Sie es ab, um ihnen zu zeigen, dass wir es reparieren müssen.


Hast du viel mit dem getestet delay time? Ich benutze 500auf meinem Galaxy S4. Ich bin mir nicht sicher, ob dies auf einem anderen Gerät ein Problem darstellen würde.
Theblang

Ich habe nicht viel mit der Verzögerungszeit 500getestet , aber ich denke, es würde gut gehen. Ich habe es auf der getestet. emulatorIch wollte nur safemit den 1000Millisekunden sein
Ahmed Hegazy

3
Es besteht keine Notwendigkeit, verspätet zu posten. du kannst einfach posten. Ohne Verzögerung zu posten bedeutet nur "Mach das, wenn du mit dem fertig bist, was du jetzt machst". und was es jetzt tut, ist das Messen und Auslegen Ihrer Benutzeroberfläche.
Oren

47

Meine Lösung besteht darin, Folgendes zu überschreiben 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
Der Vorteil Ihrer Lösung besteht darin, dass der Blickversatz unberührt bleibt! Auf diese Weise fahren wir mit dem hier angegebenen Entwurfsmuster fort: google.com/design/spec/patterns/… . Vielen Dank!
Igor de Lorenzi

Können Sie bitte erklären, wie das funktioniert? Es funktioniert, aber ich verstehe das Innenleben nicht ganz. Vermisse ich nur etwas Triviales?
Sree

setRefreshing funktioniert nur nach dem onMeasure-Aufruf, daher speichern wir das lokale Aktualisierungsflag und wenden es beim ersten onMeasure-Aufruf an
nikita.zhelonkin

5
Ich liebe diese Lösung, weil der Code, der das SwipeRefreshLayout aufruft, genau so sein kann, wie wir es uns wünschen, ohne Komplikationen. Es hat im Grunde den Fehler in SwipeRefreshLayout behoben. SwipeRefreshLayout sollte wirklich nur so implementiert werden.
DataGraham

Diese Antwort sieht vielleicht nicht so einfach aus, behebt das Problem jedoch durch viele Support-Bibliotheksversionen (in meinem Fall 23.1.1). Laut Ticket ist dieses Problem bei 23.2 nicht behoben. code.google.com/p/android/issues/detail?id=77712
Robert

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

3
Diese Antwort ist die einzige, die kein Hack ist, daher sollte sie akzeptiert werden
Heinrich

1
Nur eine kleine Sache: .removeGlobalOnLayoutListener sollte .removeOnGlobalLayoutListener sein
mkuech

1
@mkeuch hängt davon ab, auf welche API Sie abzielen. Wenn Sie auf API16 abzielen, müssen Sie die API-Versionsprüfung durchführen und beide verwenden.
Marko

Ich mag diese Antwort etwas mehr als die am besten bewertete Antwort, weil klarer ist, warum sie verwendet wird.
Marcel Bro

Ich muss mich korrigieren, diese Lösung funktioniert NICHT immer für mich. In einigen Fällen wird der Ladeindikator durch das setRefreshing(false)Einschließen von onGlobalLayoutListener()Wraps nicht verworfen.
Marcel Bro


4

Basierend auf der Antwort von Volodymyr Baydalka ist dies nur eine kleine Idee, die Ihnen hilft, den Code sauber zu halten. Ich glaube, es verdient einen Beitrag: Sie können das Verhalten leicht vom Posten zum direkten Methodenaufruf zurücksetzen, sobald der Fehler behoben ist.

Schreiben Sie eine Utility-Klasse wie:

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

In Ihrem Code ersetzen Sie mSwipeContainer.setRefreshing(isRefreshing)durch Utils.setRefreshing(mSwipeContainer, isRefreshing): Jetzt muss nur noch ein Punkt im Code geändert werden, sobald der Fehler behoben ist, die UtilsKlasse. Die Methode kann dann auch inline (und aus Utils) entfernt werden.

In der Regel ist kein visueller Unterschied erkennbar. Beachten Sie jedoch, dass die ausstehende Aktualisierung Ihre alten ActivityInstanzen am Leben erhalten kann , indem Sie die SwipeRefreshLayoutin ihren Ansichten enthaltenen Hierarchien beibehalten. Wenn dies ein Problem darstellt, optimieren Sie die Methode, um WeakReferences zu verwenden. Normalerweise blockieren Sie den UI-Thread jedoch nicht und verzögern gc daher nur um einige Millisekunden.


2

Sie können diese Methode auch vor setRefreshing aufrufen.

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

Es funktioniert für mich.


1

Ich habe AppCompat Library com.android.support:appcompat-v7:21.0.3mit demselben Ansatz verwendet und es hat funktioniert. Sie aktualisieren also die Version dieser Bibliothek.

Hinweis: RelativeLayoutUnterstützt keine Orientierung, es ist ein Attribut für 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);
}

Layout 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

Verwenden Sie neben Volodymyr Baydalka auch den folgenden Code:

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

Erläuterung Ich habe die von Volodymyr Baydalka angegebene Lösung (unter Verwendung von Fragmenten) implementiert, aber nachdem swipeReferesh gestartet wurde, ging sie auch beim Aufruf swipeContainer.setRefreshing(false); nicht verloren und musste den oben angegebenen Code implementieren, der mein Problem löste. Weitere Ideen, warum dies geschieht, sind sehr willkommen.

Grüße,

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


1

@ niks.stack Als Antwort auf seine Antwort würde ich das Fortschrittsrad zeigen, nachdem onLayout()es fertig ist. Wenn ich es direkt danach verwendete onMeasure(), wurden einige der Offsets nicht berücksichtigt, aber wenn ich es danach verwendete , wurde es verwendet 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;
    }
}

Ich suchte nach einem guten Rückruf nach onMeasure! Danke. War nervig die Scheiße aus mir, dass beim ersten Lauf der Offset aus war ...
xdbas

0

Meine Lösung (ohne Unterstützung 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

Ich benutze 'com.android.support:appcompat-v7:23.1.1'

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

Früher habe ich die swipeRefreshLayout.setRefreshing(true);Inside- getData()Methode verwendet, also hat es nicht funktioniert. Ich weiß nicht, warum es nicht innerhalb der Methode funktioniert.

Obwohl ich swipeRefreshLayout.setRefreshing(true);nur einmal in meinem Fragment verwendet habe.


0

Versuche dies

mSwipeRefreshLayout.setNestedScrollingEnabled (true);


-1

Eine weitere Problemumgehung besteht darin, ein neues Steuerelement zu erstellen und von SwipeRefreshLayout abzuleiten. Überschreiben Sie die OnMeasure-Funktion und schalten Sie die Aktualisierung erneut um, wenn die Aktualisierung aktiviert ist. Ich verwende diese Lösung in einem Xamarin-Projekt und sie funktioniert gut. Hier ein Beispiel für einen C # -Code:

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;
            }
        }
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.