SwipeRefreshLayout setRefreshing () không hiển thị chỉ báo ban đầu


144

Tôi có một bố cục rất đơn giản nhưng khi tôi gọi setRefreshing(true)vào onActivityCreated()đoạn của mình, nó không hiển thị ban đầu.

Nó chỉ hiển thị khi tôi thực hiện kéo để làm mới. Bất cứ ý tưởng tại sao nó không hiển thị ban đầu?

Mảnh 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>

Mã phân đoạn:

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

Bạn sử dụng phiên bản nào?
Ahmed Hegazy

biên dịch "com.android.support:appcompat-v7:21.0.0"
sấm sétNinja

Tôi xác nhận rằng vấn đề này đã xảy ra với tôi từ phiên bản đó. Các phiên bản trước đó không có bất kỳ vấn đề nào với điều đó. Tôi sẽ đăng giải pháp nếu tôi nhận được bất kỳ.
Ahmed Hegazy

Hãy để tôi thử phiên bản cũ hơn
sấm sétNinja

Cũng không làm việc trên v20 cho tôi. Phiên bản nào nó làm việc cho bạn trên?
sấm sétNinja

Câu trả lời:


307

Đối mặt với vấn đề tương tự. Giải pháp của tôi -

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

1
Hoạt động tốt nhưng tôi không biết tại sao chúng ta cần phải làm điều này thay vì mSwipeRefreshLayout.setRefreshing (đúng);
Cocorico


1
Đó không phải là một giải pháp / giải pháp tốt. Nếu người dùng ở chế độ trên máy bay, refreshLayout của bạn sẽ bắt đầu làm mới trên luồng của chính nó sau khi đã bắt đầu yêu cầu mạng và nhận được phản hồi của nó (thất bại trong trường hợp này). Khi xử lý nó để dừng refreshLayout, nó sẽ không hoạt động vì nó chưa bắt đầu! Nói cách khác, refreshLayout sẽ bắt đầu làm mới sau khi bạn nhận được phản hồi của mình. Chúc may mắn ngăn chặn nó
Samer

1
Theo tôi nhớ "bài viết" là đồng bộ hóa và chạy theo thứ tự thêm. Vì vậy, bạn có thể thêm một bài viết để ngăn chặn nó.
Volodymyr Baydalka

2
Nó có thể trông đơn giản nhất trong việc thực hiện nhưng nó không đẹp. Giải pháp @ niks.stack bên dưới tốt hơn vì không yêu cầu bất kỳ thay đổi nào trong việc sử dụng mã, vì vậy khi cuối cùng lỗi này được khắc phục trong hỗ trợ lib, bạn chỉ cần quay lại hỗ trợ lib SwipeRefreshLayout
Marcin

100

Thay vào đó, hãy xem câu trả lời của Volodymyr Baydalka.

Đây là những cách giải quyết cũ.

Điều đó đã từng hoạt động trên phiên bản trước đó android.support.v4, nhưng từ phiên bản 21.0.0 đang diễn ra, nó không hoạt động và vẫn tồn tại với bản android.support.v4:21.0.3phát hành vào ngày 10-12 tháng 12 năm 2014 và đây là lý do.

Chỉ báo SwipeRefreshLayout không xuất hiện khi setRefreshing(true)được gọi trướcSwipeRefreshLayout.onMeasure()

Cách giải quyết:

gọi setProgressViewOffset()trên SwipeRefreshLayoutđó invalidtes quan điểm vòng tròn của cách bố trí gây SwipeRefreshLayout.onMeasure()được gọi ngay lập tức.

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

CẬP NHẬT Giải pháp tốt hơn

Bởi vì thanh hành động có thể mỏng hơn khi thay đổi hướng hoặc bạn đã đặt kích thước thanh hành động theo cách thủ công. Chúng tôi đặt phần bù theo pixel từ đầu của chế độ xem này, tại đó trình điều khiển tiến trình sẽ đến để đặt lại sau khi thực hiện thao tác vuốt thành công với kích thước thanh hành động hiện tại.

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

CẬP NHẬT ngày 20 tháng 11 năm 2014

Nếu ứng dụng của bạn không hiển thị SwipeRefreshLayout thì điều đó không quan trọng lắm khi chế độ xem được bắt đầu. Bạn chỉ có thể đăng nó vào một thời điểm trong tương lai bằng cách sử dụng trình xử lý hoặc bất kỳ điều gì bạn muốn.

làm ví dụ

handler.postDelayed(new Runnable() {

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

hoặc như câu trả lời của Volodymyr Baydalka đã đề cập.

Đây là vấn đề trong trình theo dõi vấn đề Android. Vui lòng nâng cấp nó để cho họ thấy rằng chúng tôi cần sửa nó.


Bạn đã làm nhiều thử nghiệm với delay time? Tôi đang sử dụng 500trên của tôi Galaxy S4. Không chắc chắn nếu điều này sẽ là một vấn đề trên một thiết bị khác.
theblang

Tôi đã không làm nhiều thử nghiệm với thời gian trì hoãn, nhưng tôi nghĩ 500sẽ làm tốt. Tôi đã thử nghiệm nó trên. emulatorTôi chỉ muốn safeở trong một 1000phần nghìn giây
Ahmed Hegazy

3
Không cần phải trì hoãn. bạn chỉ có thể đăng bài. đăng không chậm trễ chỉ có nghĩa là "làm điều này một khi bạn đã hoàn thành với những gì bạn đang làm bây giờ". và những gì nó đang làm bây giờ là đo lường và đưa ra UI của bạn.
Oren

47

Giải pháp của tôi là ghi đè 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
Ưu điểm của giải pháp của bạn là giữ cho chế độ xem không bị ảnh hưởng! Bằng cách này, chúng tôi tiếp tục tuân thủ mẫu thiết kế được chỉ định tại đây: google.com/design/spec/potypes/iêu . Cảm ơn!
Igor de Lorenzi

Bạn có thể vui lòng giải thích làm thế nào điều này hoạt động? Nó đang hoạt động nhưng tôi không hoàn toàn có được hoạt động bên trong. Có phải tôi chỉ thiếu một cái gì đó tầm thường?
Sree

setRefreshing chỉ hoạt động sau cuộc gọi onMeasure, vì vậy chúng tôi lưu trữ cờ làm mới cục bộ và trên cuộc gọi onMeasure đầu tiên áp dụng nó
nikita.zhelonkin

5
Tôi thích giải pháp này bởi vì nó có nghĩa là mã gọi SwipeRefreshLayout có thể chính xác như chúng ta mong muốn, không có bất kỳ biến chứng nào. Về cơ bản, nó đã sửa lỗi trong SwipeRefreshLayout. SwipeRefreshLayout thực sự chỉ nên được thực hiện theo cách này.
DataGraham 8/07/2015

Câu trả lời này có thể không đơn giản như vậy, nhưng nó khắc phục vấn đề độc đáo thông qua nhiều phiên bản thư viện hỗ trợ (trong trường hợp của tôi là 23.1.1). Theo vé, vấn đề này không được khắc phục @ 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);
                            }
                        });

3
Câu trả lời này là câu duy nhất không phải là hack vì vậy nó nên được chấp nhận
Heinrich

1
Chỉ là một điều nhỏ: .removeGlobalOnLayoutListener phải là .removeOnGlobalLayoutListener
mkuech

1
@mkeuch phụ thuộc vào API mà bạn nhắm mục tiêu. Nếu nhắm mục tiêu của bạn theo API16, bạn phải kiểm tra phiên bản API và sử dụng cả hai.
Marko

Tôi thích câu này hơn một chút so với câu trả lời được đánh giá cao nhất bởi vì nó rõ ràng hơn tại sao nó được sử dụng.
Marcel Bro

Tôi phải tự sửa, giải pháp này KHÔNG phải lúc nào cũng hiệu quả với tôi. Trong một số trường hợp, việc gọi setRefreshing(false)được bọc trong onGlobalLayoutListener()không bỏ qua chỉ báo tải.
Marcel Bro


4

Dựa trên câu trả lời của Volodymyr Baydalka , đây chỉ là một ý tưởng nhỏ sẽ giúp bạn giữ sạch mã. Nó xứng đáng với một bài đăng tôi tin rằng: bạn sẽ có thể hoàn nguyên hành vi một cách dễ dàng từ việc đăng lên cuộc gọi phương thức trực tiếp, một khi lỗi đã được sửa.

Viết một lớp tiện ích như:

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

Trong mã của bạn thay thế mSwipeContainer.setRefreshing(isRefreshing)bằng Utils.setRefreshing(mSwipeContainer, isRefreshing): bây giờ chỉ cần thay đổi một điểm trong mã khi lỗi được sửa, Utilslớp. Phương thức cũng có thể được nội tuyến sau đó (và loại bỏ khỏi Utils).

Thường sẽ không có sự khác biệt đáng chú ý về thị giác. Hãy ghi nhớ rằng việc làm mới đang chờ xử lý có thể giúp duy trì các Activityphiên bản cũ của bạn , giữ nguyên cấu trúc SwipeRefreshLayoutphân cấp trong chế độ xem của chúng. Nếu đó là một vấn đề đáng lo ngại, hãy điều chỉnh phương thức để sử dụng WeakReferences, nhưng thông thường bạn không chặn luồng UI, và do đó chỉ trì hoãn gc trong vài mili giây.


2

Ngoài ra, bạn có thể gọi phương thức này trước khi setRefreshing ..

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

Nó workef cho tôi.


1

Tôi đã sử dụng Thư viện AppCompat com.android.support:appcompat-v7:21.0.3, sử dụng cùng phương pháp của bạn và nó đã hoạt động. Vì vậy, bạn cập nhật phiên bản của thư viện đó.

Lời khuyên: RelativeLayoutkhông hỗ trợ định hướng, đó là một thuộc tính cho 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);
}

Bố cục 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

Ngoài Volodymyr Baydalka cũng sử dụng mã sau đây:

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

Giải thích Tôi đã triển khai giải pháp được đưa ra bởi Volodymyr Baydalka (sử dụng các đoạn) nhưng sau khi vuốtReferesh bắt đầu, nó không bao giờ biến mất ngay cả khi gọi swipeContainer.setRefreshing(false); nên phải thực hiện mã được đưa ra ở trên để giải quyết vấn đề của tôi. bất kỳ ý tưởng nào tại sao điều này xảy ra được chào đón nhất.

Trân trọng,

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


1

@ niks.stack Đáp lại câu trả lời của anh ấy, tôi sẽ chỉ ra bánh xe tiến bộ sau khi onLayout()đã hoàn thành. Khi tôi sử dụng nó trực tiếp sau onMeasure()đó, nó sẽ không tôn trọng một số điểm bù trừ, nhưng sử dụng nó sau 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;
    }
}

Tôi đã tìm kiếm một cuộc gọi lại tốt sau khi onMeasure! Thnx. Thật khó chịu với tôi rằng trong lần chạy đầu tiên, phần bù đã tắt ...
xdbas 10/03/2016

0

Giải pháp của tôi (không hỗ trợ 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

Tôi đang sử dụng 'com.android.support:appcompat-v7:23.1.1'

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

trước đây tôi đã sử dụng phương thức swipeRefreshLayout.setRefreshing(true);bên trong getData(), vì vậy nó không hoạt động. Tôi không biết tại sao nó không hoạt động bên trong phương pháp.

Mặc dù tôi swipeRefreshLayout.setRefreshing(true);chỉ sử dụng một lần trong Fragment của mình.


0

Thử cái này

mSwipeRefreshLayout.setNestedScrollingEnables (true);


-1

Một cách giải quyết khác là tạo ra một điều khiển và chuyển đổi mới từ SwipeRefreshLayout. Ghi đè chức năng OnMeasure và chuyển đổi làm mới một lần nữa, nếu làm mới được kích hoạt. Tôi sử dụng giải pháp này trong một dự án xamarin và nó hoạt động tốt. Dưới đây là một số mẫu C # -Mã:

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;
            }
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.