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

คุณใช้เวอร์ชั่นอะไร
Ahmed Hegazy

รวบรวม "com.android.support:appcompat-v7:21.0.0"
thunderousNinja

ฉันยืนยันว่าปัญหานี้เกิดขึ้นกับฉันจากเวอร์ชันนั้นต่อไป เวอร์ชันก่อนหน้านี้ไม่มีปัญหาใด ๆ ฉันจะโพสต์วิธีแก้ปัญหาถ้าฉันได้รับใด ๆ
Ahmed Hegazy

ให้ฉันลองใช้เวอร์ชันก่อนหน้า
thunderousNinja

ยังไม่ทำงานกับ v20 สำหรับฉัน เวอร์ชั่นไหนที่ใช้งานได้สำหรับคุณ?
thunderousNinja

คำตอบ:


307

ต้องเผชิญกับปัญหาเดียวกัน โซลูชันของฉัน -

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

1
ทำงานได้ดี แต่ฉันไม่รู้ว่าทำไมเราต้องทำเช่นนี้แทน mSwipeRefreshLayout.setRefreshing (จริง);
Cocorico


1
นั่นไม่ใช่ทางออก / วิธีแก้ปัญหาที่ดีหากผู้ใช้อยู่ในโหมดเครื่องบินการรีเฟรชเลย์เอาต์ของคุณจะเริ่มต้นการรีเฟรชในเธรดของตัวเองหลังจากเริ่มคำขอเครือข่ายแล้วและได้รับการตอบกลับ (ความล้มเหลวในกรณีนี้) ในการจัดการมันเพื่อหยุดการรีเฟรชเลย์เอาต์มันจะไม่ทำงานเพราะมันยังไม่เริ่ม! กล่าวอีกนัยหนึ่งการรีเฟรชเลย์เอาต์จะเริ่มรีเฟรชหลังจากที่คุณได้รับคำตอบ ขอให้โชคดีหยุดมัน
Samer

1
ในขณะที่ฉันจำได้ว่า "โพสต์" ซิงค์และทำงานตามลำดับการเพิ่ม ดังนั้นคุณสามารถเพิ่มโพสต์อื่นเพื่อหยุด
Volodymyr Baydalka

2
มันอาจดูง่ายที่สุดในการติดตั้ง แต่ก็ไม่ดี วิธีแก้ปัญหา @ niks.stack ด้านล่างดีกว่าเพราะไม่ต้องการการเปลี่ยนแปลงใด ๆ ของรหัสที่ใช้ดังนั้นเมื่อในที่สุดข้อผิดพลาดนี้ได้รับการแก้ไขใน lib สนับสนุนคุณเพียงแค่เปลี่ยนกลับไปรองรับ lib SwipeRefreshLayout
Marcin Orlowski

100

ดูคำตอบของ Volodymyr Baydalka แทน

นี่เป็นวิธีแก้ปัญหาแบบเดิม

ที่ใช้ในการทำงานกับรุ่นก่อนหน้าของandroid.support.v4แต่จากรุ่น 21.0.0 อย่างต่อเนื่องมันไม่ทำงานและยังคงอยู่กับการandroid.support.v4:21.0.3เปิดตัวที่10-12 ธันวาคม 2014และนี่คือเหตุผล

ตัวบ่งชี้ SwipeRefreshLayout ไม่ปรากฏขึ้นเมื่อsetRefreshing(true)ถูกเรียกก่อนSwipeRefreshLayout.onMeasure()

การแก้ปัญหา:

การเรียกsetProgressViewOffset()ใช้SwipeRefreshLayoutมุมมองวงกลมของเค้าโครงทำให้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 พ.ย. 2557

หากแอปของคุณไม่สำคัญมากในการแสดง SwipeRefreshLayout เมื่อมุมมองเริ่มต้นขึ้น คุณสามารถโพสต์ไปที่เวลาในอนาคตโดยใช้เครื่องมือจัดการหรือสิ่งที่คุณต้องการ

ตัวอย่างเช่น.

handler.postDelayed(new Runnable() {

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

หรือตามคำตอบของ Volodymyr Baydalka

นี่คือปัญหาในตัวติดตามปัญหา android โปรดโหวตขึ้นเพื่อแสดงว่าเราต้องการได้รับการแก้ไข


คุณทำมากการทดสอบด้วยdelay time? ฉันใช้ของฉัน500 Galaxy S4ไม่แน่ใจว่านี่จะเป็นปัญหาของอุปกรณ์อื่นหรือไม่
theblang

ฉันไม่ได้ทำการทดสอบมากนักกับเวลาหน่วง แต่ฉันคิดว่า500จะทำได้ดีฉันได้ทำการทดสอบกับemulatorฉันฉันแค่อยากจะอยู่safeกับ1000มิลลิวินาที
Ahmed Hegazy

3
ไม่จำเป็นต้องโพสต์ล่าช้า คุณสามารถโพสต์ได้ การโพสต์โดยไม่ล่าช้าหมายถึง "ทำสิ่งนี้เมื่อคุณทำสิ่งที่คุณทำเสร็จแล้ว" และสิ่งที่กำลังทำอยู่ตอนนี้คือการวัดและกำหนด UI ของคุณ
Oren

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/... ขอบคุณ!
Igor de Lorenzi

คุณช่วยอธิบายวิธีการทำงานของมันได้มั้ย มันใช้งานได้ แต่ฉันไม่ค่อยได้ผลงานภายใน ฉันเพิ่งจะพลาดสิ่งเล็กน้อยไปหรือเปล่า
Sree

setRefreshing ใช้งานได้หลังจากการโทร onMeasure เท่านั้นดังนั้นเราจึงจัดเก็บการรีเฟรชแฟล็กท้องถิ่นและในการเรียกใช้ onMeasure ครั้งแรก
nikita.zhelonkin

5
ฉันชอบโซลูชันนี้เพราะมันหมายถึงรหัสที่เรียกว่า SwipeRefreshLayout นั้นเป็นไปตามที่เราต้องการโดยไม่มีปัญหาใด ๆ มันเป็นพื้นแก้ไขข้อผิดพลาดใน SwipeRefreshLayout SwipeRefreshLayout ควรนำมาใช้จริงด้วยวิธีนี้
DataGraham

คำตอบนี้อาจดูไม่ตรงไปตรงมา แต่จะแก้ไขปัญหาผ่านทางไลบรารีการสนับสนุนหลายรุ่น (ในกรณีของฉันคือ 23.1.1) ปัญหานี้ไม่ได้รับการแก้ไข @ 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
คำตอบนี้เป็นคำตอบเดียวที่ไม่ใช่แฮ็คดังนั้นจึงควรได้รับการยอมรับ
Heinrich

1
เพียงเล็กน้อย: .removeGlobalOnLayoutListener ควรเป็น. RemoveOnGlobalLayoutListener
mkuech

1
@mkeuch ขึ้นอยู่กับการกำหนดเป้าหมาย API ของคุณ หากการกำหนดเป้าหมายภายใต้ API16 คุณต้องทำการตรวจสอบเวอร์ชัน API และใช้ทั้งคู่
Marko

ฉันชอบสิ่งนี้มากกว่าคำตอบ upvoted มากที่สุดเพราะชัดเจนว่าทำไมจึงใช้
Marcel Bro

ฉันต้องแก้ไขตัวเองวิธีนี้ไม่ได้ผลกับฉันเสมอไป ในบางกรณีการโทรที่setRefreshing(false)ถูกหุ้มไว้onGlobalLayoutListener()จะไม่ยกเลิกตัวบ่งชี้การโหลด
Marcel Bro


4

ขึ้นอยู่กับคำตอบของ Volodymyr Baydalkaนี่เป็นความคิดเล็ก ๆ ที่จะช่วยให้คุณรักษาโค้ดให้สะอาดอยู่เสมอ มันสมควรโพสต์ที่ฉันเชื่อว่า: คุณจะสามารถเปลี่ยนพฤติกรรมได้อย่างง่ายดายจากการโพสต์เป็นการเรียกเมธอดโดยตรงเมื่อข้อผิดพลาดได้รับการแก้ไข

เขียนคลาสยูทิลิตี้เช่น:

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ชั้นเรียน เมธอดยังสามารถถูก inline (และลบออกจากUtils)

โดยทั่วไปจะไม่มีความแตกต่างที่เห็นได้ชัดเจน โปรดทราบว่าการรีเฟรชที่รอดำเนินการอาจทำให้Activityอินสแตนซ์เก่าของคุณยังมีชีวิตอยู่โดยถือไว้SwipeRefreshLayoutในลำดับชั้นการดู หากเป็นสิ่งที่น่ากังวลให้ปรับแต่งวิธีการใช้WeakReferences แต่โดยปกติแล้วคุณจะไม่บล็อกเธรด UI ต่อไปและทำให้ gc หน่วงเวลาสองสามมิลลิวินาทีเท่านั้น


2

นอกจากนี้คุณสามารถเรียกวิธีนี้ก่อน setRefreshing ..

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

มันทำงานได้สำหรับฉัน


1

ฉันใช้ AppCompat Library com.android.support:appcompat-v7:21.0.3โดยใช้วิธีการเดียวกันกับคุณ ดังนั้นคุณจะอัพเดตเวอร์ชันของไลบรารี่นั้น

คำแนะนำ: ไม่สนับสนุนการปฐมนิเทศก็แอตทริบิวต์สำหรับRelativeLayoutLinearLayout

@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

นอกจากนี้ Volodymyr Baydalka ใช้รหัสต่อไปนี้เช่นกัน:

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

คำอธิบาย ที่ผมดำเนินการแก้ปัญหาที่ได้รับจากโล Baydalka (โดยใช้เศษ) แต่หลังจาก swipeReferesh เริ่มต้นมันไม่เคยจากไปแม้ในโทรswipeContainer.setRefreshing(false); เพื่อให้มีการใช้รหัสที่กำหนดข้างต้นซึ่งแก้ปัญหาของฉัน แนวคิดเพิ่มเติมใด ๆ ที่ทำให้เกิดเหตุการณ์นี้ยินดีต้อนรับมากที่สุด

ความนับถือ,

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


1

@ niks.stack ตามคำตอบของเขาฉันจะแสดงกงล้อความคืบหน้าหลังจากonLayout()เสร็จสิ้น เมื่อฉันใช้มันโดยตรงonMeasure()มันจะไม่ให้เกียรติ offsets บางส่วน แต่จะใช้หลังจาก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! ขอบคุณ น่ารำคาญอึออกมาจากฉันว่าในการเริ่มต้นการทำงานออฟเซ็ตถูกปิด ...
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 และสลับการรีเฟรชอีกครั้งหากเปิดใช้งานการรีเฟรช ฉันใช้โซลูชันนี้ในโครงการซามารินและใช้งานได้ดี นี่คือตัวอย่าง 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;
            }
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.