SwipeRefreshLayout setRefreshing () tidak menunjukkan indikator pada awalnya

144

Saya memiliki tata letak yang sangat sederhana tapi ketika saya sebut setRefreshing(true)di onActivityCreated()fragmen saya, itu tidak menunjukkan awalnya.

Itu hanya menunjukkan ketika saya melakukan tarik untuk menyegarkan. Adakah ide mengapa ini tidak muncul pada awalnya?

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

Kode fragmen:

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
    }
}
Ninja guntur
sumber
Versi apa yang Anda gunakan?
Ahmed Hegazy
kompilasi "com.android.support:appcompat-v7:21.0.0"
thunderousNinja
Saya mengkonfirmasi bahwa masalah ini terjadi pada saya sejak versi tersebut berjalan. Versi sebelumnya tidak memiliki masalah dengan itu. Saya akan memposting solusinya jika saya mendapatkannya.
Ahmed Hegazy
Biarkan saya mencoba versi sebelumnya
thunderousNinja
Juga tidak bekerja pada v20 untuk saya. Versi apa yang cocok untuk Anda?
thunderousNinja

Jawaban:

307

Menghadapi masalah yang sama. Solusi saya -

mSwipeRefreshLayout.post(new Runnable() {
    @Override
    public void run() {
        mSwipeRefreshLayout.setRefreshing(true);
    }
});
Volodymyr Baydalka
sumber
1
Berfungsi dengan baik tetapi saya tidak tahu mengapa kita perlu melakukan ini daripada mSwipeRefreshLayout.setRefreshing (true);
Cocorico
1
Itu bukan solusi / solusi yang baik. Jika pengguna dalam mode pesawat, refreshLayout Anda akan mulai menyegarkan di utasnya sendiri setelah memulai permintaan jaringan dan menerima responsnya (kegagalan dalam kasus ini). Saat menanganinya untuk menghentikan refreshLayout, itu tidak akan berhasil karena belum dimulai! Dengan kata lain refreshLayout akan mulai menyegarkan setelah Anda menerima respons Anda. Semoga berhasil menghentikannya
Samer
1
Seingat saya "posting" disinkronkan dan berjalan sesuai urutan penambahan. Jadi, Anda dapat menambahkan pos lain untuk menghentikannya.
Volodymyr Baydalka
2
Mungkin terlihat paling sederhana dalam implementasi tetapi tidak bagus. @ solusi niks.stack di bawah ini lebih baik karena tidak memerlukan perubahan dalam kode yang menggunakannya, jadi ketika akhirnya bug ini diperbaiki dalam lib dukungan, Anda cukup beralih kembali untuk mendukung lib SwipeRefreshLayout
Marcin Orlowski
100

Lihat jawaban Volodymyr Baydalka sebagai gantinya.

Ini adalah solusi lama.

Itu digunakan untuk bekerja pada versi sebelumnya android.support.v4, tetapi dari versi 21.0.0 yang sedang berjalan itu tidak berfungsi dan masih ada dengan android.support.v4:21.0.3dirilis pada 10-12 Desember 2014 dan ini adalah alasannya.

Indikator SwipeRefreshLayout tidak muncul ketika setRefreshing(true)dipanggil sebelumSwipeRefreshLayout.onMeasure()

Penanganan masalah:

memanggil setProgressViewOffset()pada SwipeRefreshLayoutyang tidak valid tampilan lingkaran dari tata letak menyebabkan SwipeRefreshLayout.onMeasure()dipanggil segera.

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

UPDATE Solusi yang Lebih Baik

Karena bilah tindakan bisa menjadi lebih tipis ketika orientasi berubah atau Anda telah menetapkan ukuran bilah tindakan secara manual. Kami menetapkan offset dalam piksel dari bagian atas tampilan ini tempat pemintal progres harus datang untuk mengatur ulang setelah gerakan gesek yang berhasil ke ukuran bilah tindakan saat ini.

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

Jika itu tidak terlalu penting untuk aplikasi Anda untuk menunjukkan SwipeRefreshLayout setelah tampilan dimulai. Anda dapat mempostingnya di waktu mendatang dengan menggunakan penangan atau hal lain yang Anda inginkan.

sebagai contoh.

handler.postDelayed(new Runnable() {

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

atau seperti jawaban Volodymyr Baydalka.

Inilah masalah dalam pelacak masalah android. Harap perbarui untuk menunjukkan kepada mereka bahwa kami perlu memperbaikinya.

Ahmed Hegazy
sumber
Apakah Anda melakukan banyak pengujian dengan delay time? Saya menggunakan 500pada saya Galaxy S4. Tidak yakin apakah ini akan menjadi masalah pada perangkat lain.
theblang
Saya tidak melakukan banyak pengujian dengan waktu tunda, tetapi saya pikir 500akan baik-baik saja. Saya telah mengujinya pada. emulatorSaya hanya ingin safebersama 1000milidetik
Ahmed Hegazy
3
Tidak perlu memposting ditunda. Anda bisa memposting. memposting tanpa penundaan hanya berarti "lakukan hal ini setelah Anda selesai dengan apa yang Anda lakukan sekarang". dan apa yang dilakukannya sekarang adalah mengukur dan mengatur UI Anda.
Oren
47

Solusi saya adalah mengganti 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;
        }
    }
}
nikita.zhelonkin
sumber
3
Keuntungan dari solusi Anda adalah untuk menjaga tampilan offset tidak tersentuh! Dengan cara ini kami melanjutkan sesuai dengan pola desain yang ditentukan di sini: google.com/design/spec/patterns/… . Terima kasih!
Igor de Lorenzi
Bisakah Anda jelaskan bagaimana cara kerjanya? Ini bekerja tetapi saya tidak cukup mendapatkan pekerjaan batin. Apakah saya hanya melewatkan sesuatu yang sepele?
Sree
setRefreshing hanya berfungsi setelah panggilan onMeasure, jadi kami menyimpan bendera penyegaran lokal, dan pada panggilan onMeasure pertama, terapkan itu
nikita.zhelonkin
5
Saya suka solusi ini karena itu berarti kode memanggil SwipeRefreshLayout dapat persis seperti yang kita inginkan, tanpa komplikasi. Ini pada dasarnya memperbaiki bug di SwipeRefreshLayout. SwipeRefreshLayout benar-benar harus diimplementasikan dengan cara ini.
DataGraham
Jawaban ini mungkin terlihat tidak langsung, tetapi ini memperbaiki masalah dengan baik melalui banyak versi perpustakaan dukungan (dalam kasus saya ini 23.1.1). Menurut tiket, masalah ini tidak diperbaiki @ 23.2. 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);
                            }
                        });
baoyz
sumber
3
Jawaban ini adalah satu-satunya yang bukan retasan sehingga harus diterima
Heinrich
1
Hanya hal kecil: .removeGlobalOnLayoutListener harus .removeOnGlobalLayoutListener
mkuech
1
@mkeuch tergantung pada API penargetan Anda. Jika penargetan Anda di bawah API16, Anda harus melakukan pemeriksaan versi API, dan menggunakan keduanya.
Marko
Saya suka yang ini sedikit lebih banyak daripada jawaban yang paling banyak dipilih karena lebih jelas mengapa ini digunakan.
Marcel Bro
Saya harus memperbaiki sendiri, solusi ini TIDAK selalu bekerja untuk saya. Dalam beberapa kasus, panggilan yang setRefreshing(false)dibungkus onGlobalLayoutListener()tidak menghilangkan indikator pengisian.
Marcel Bro
4

Berdasarkan jawaban Volodymyr Baydalka , ini hanya ide kecil yang akan membantu Anda menjaga kode tetap bersih. Saya layak menerima posting yang saya yakin: Anda dapat dengan mudah mengembalikan perilaku dari memposting ke panggilan metode langsung, setelah bug diperbaiki.

Tulis kelas utilitas seperti:

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

Dalam pengganti kode Anda mSwipeContainer.setRefreshing(isRefreshing)dengan Utils.setRefreshing(mSwipeContainer, isRefreshing): sekarang hanya satu titik dalam kode yang perlu diubah setelah bug diperbaiki, Utilskelas. Metode ini juga dapat diuraikan (dan dihapus dari Utils).

Biasanya tidak ada perbedaan visual yang nyata. Perlu diingat bahwa penyegaran yang tertunda dapat tetap menghidupkan Activityinstance lama Anda , berpegang pada SwipeRefreshLayouthierarki tampilan mereka. Jika itu adalah masalah, atur metode untuk menggunakan WeakReferences, tetapi biasanya Anda tidak memblokir utas UI, dan karenanya menunda gc hanya dengan beberapa milidetik.

Gil Vegliach
sumber
2

Anda juga dapat memanggil metode ini sebelum setRefreshing ..

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

Ini berhasil bagi saya.

Leonardo Roese
sumber
1

Saya menggunakan Perpustakaan AppCompat com.android.support:appcompat-v7:21.0.3, menggunakan pendekatan yang sama dan berhasil. Jadi, Anda memperbarui versi perpustakaan itu.

Saran: RelativeLayouttidak mendukung orientasi, itu adalah atribut untuk 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>
Jesús Castro
sumber
1

Selain Volodymyr Baydalka, gunakan kode berikut juga:

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

Penjelasan Saya menerapkan solusi yang diberikan oleh Volodymyr Baydalka (menggunakan fragmen) tetapi setelah swipeReferesh mulai, itu tidak pernah hilang bahkan pada panggilan swipeContainer.setRefreshing(false); jadi harus mengimplementasikan kode yang diberikan di atas yang memecahkan masalah saya. ada lebih banyak ide mengapa hal ini terjadi sangat disambut.

Salam,

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

Junaid
sumber
1

@ niks.stack Menanggapi jawabannya, saya akan menunjukkan roda progres setelah onLayout()selesai. Ketika saya menggunakannya langsung setelah onMeasure()itu, itu tidak akan menghormati sebagian dari offset, tetapi menggunakannya setelah onLayout()itu.

@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;
    }
}
mco
sumber
Saya mencari panggilan balik yang bagus setelah onMeasure! Thnx.
Sungguh
0

Solusi saya (tanpa dukungan 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);
Artrmz
sumber
0

Saya menggunakan 'com.android.support:appcompat-v7:23.1.1'

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

sebelumnya saya menggunakan metode swipeRefreshLayout.setRefreshing(true);inside getData(), jadi tidak berfungsi. Saya tidak tahu mengapa itu tidak bekerja di dalam metode.

Meskipun saya swipeRefreshLayout.setRefreshing(true);hanya menggunakan sekali dalam Fragmen saya.

Chinmay
sumber
0

Coba ini

mSwipeRefreshLayout.setNestedScrollingEnabled (true);

Ashwin H
sumber
-1

Solusi lain adalah membuat kontrol dan deriver baru dari SwipeRefreshLayout. Ganti fungsi OnMeasure dan aktifkan kembali penyegaran, jika penyegaran diaktifkan. Saya menggunakan solusi ini dalam proyek xamarin dan ini bekerja dengan baik. Berikut beberapa contoh C # -Kode:

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;
            }
        }
    }
}
alchy
sumber