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

编译“ com.android.support:appcompat-v7:21.0.0”
thunderousNinja

我确认从该版本开始,这个问题就一直发生在我身上。早期版本对此没有任何问题。如果有问题,我将发布解决方案。
Ahmed Hegazy 2014年

让我尝试一个更早的版本
thunderousNinja

对我来说,也不支持v20。它适用于哪个版本?
雷声

Answers:


307

面对同样的问题。我的解决方案-

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

1
效果很好,但是我不知道为什么我们需要这样做而不是mSwipeRefreshLayout.setRefreshing(true);
Cocorico 2015年


1
这不是一个好的解决方案/解决方法。如果用户处于飞行模式,则您的refreshLayout将在已经启动网络请求并收到响应后开始在其自己的线程上刷新(在这种情况下失败)。在处理它以停止refreshLayout时,由于它尚未启动,因此将不起作用!换句话说,在收到响应后,refreshLayout将开始刷新。祝你好运
萨默尔

1
我记得“帖子”是同步的,并且按照添加的顺序运行。因此,您可以添加其他帖子以使其停止。
Volodymyr Baydalka '16

2
它可能看起来最简单的实现,但不是很好。下面的 @ niks.stack解决方案更好,因为不需要使用它的代码进行任何更改,因此,最终在支持lib中修复此错误时,您只需切换回支持lib SwipeRefreshLayout
Marcin Orlowski

100

请参阅Volodymyr Baydalka的答案。

这些是旧的解决方法。

该功能过去曾在的早期版本上运行android.support.v4,但是从21.0.0版本开始一直无法使用,并且仍然存在,android.support.v4:21.0.3并于2014年12月10日至12日发布这就是原因。

setRefreshing(true)在之前调用时,SwipeRefreshLayout指示符不出现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));

更新2014年11月20日

如果启动视图后显示SwipeRefreshLayout对您的应用不是很关键。您可以使用处理程序或您想要的任何东西将其发布到将来的某个时间。

举个例子。

handler.postDelayed(new Runnable() {

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

或如沃拉迪米尔·贝达卡(Volodymyr Baydalka)的回答所述。

这里是问题在Android问题跟踪。请对其进行投票,向他们表明我们需要对其进行修复。


您对做了很多测试delay time吗?我正在使用500我的Galaxy S4。不知道这是否会在另一台设备上出现问题。
theblang 2014年

我没有对延迟时间进行太多测试,但是我认为500可以。我已经在.NET上进行了测试emulatorsafe1000毫秒
阿哈迈德·赫加齐

3
无需延迟发布。您可以发布。毫不拖延地发布信息只是意味着“一旦完成现在要做的事情,就去做”。现在它正在做的是测量和布置用户界面。
Oren 2015年

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/…。谢谢!
伊戈尔·德洛伦兹

您能解释一下这是如何工作的吗?它正在工作,但我不太了解内部工作。我只是在想些琐碎的事吗?
2015年

setRefreshing仅在onMeasure调用之后才起作用,因此我们存储了本地刷新标志,并在第一次onMeasure调用时应用它
nikita.zhelonkin 2015年

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
此答案是唯一的答案,不是hack,所以应该接受
Heinrich

1
只是一件小事:.removeGlobalOnLayoutListener应该是.removeOnGlobalLayoutListener
mkuech 2015年

1
@mkeuch取决于您的目标API。如果您的目标是使用API​​16,则必须进行API版本检查,并同时使用两者。
Marko

我最喜欢这个答案,而不是最喜欢的答案,因为它更清楚了为什么使用它。
Marcel Bro

我必须纠正自己,这种解决方案并不总是对我有用。在某些情况下,setRefreshing(false)包裹调用onGlobalLayoutListener()不会消除加载指示器。
马塞尔兄弟


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类,只需更改代码中的一点。然后也可以内联该方法(并从中删除Utils)。

通常不会有明显的视觉差异。请记住,尽管挂起的刷新可以使旧Activity实例保持活动状态,并保持SwipeRefreshLayout其视图层次结构。如果这是一个问题,请调整方法以使用WeakReferences,但通常您无论如何都不会阻塞UI线程,因此只会将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

除Volodymyr Baydalka外,还使用以下代码:

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

说明 我实现了Volodymyr Baydalka提供的解决方案(使用片段),但是在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之后,我一直在寻找良好的回调!谢谢 让我烦恼的是,在最初的运行中,偏移量已经消失了
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);只在片段中使用过一次。


0

试试这个

mSwipeRefreshLayout.setNestedScrollingEnabled(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;
            }
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.