在刷新期间使用SwipeRefreshLayout切换片段时,片段会冻结,但实际上仍然可以工作


73

更新:我认为它工作正常。但是经过一些测试后,问题仍然存在*嗅*

然后,我制作了一个简单的版本,以查看确切发生了什么,并且我知道应该分离的令人耳目一新的片段仍然留在那里。或者恰好是旧片段的视图留在新片段之上。由于我原始应用程序的RecyclerView的背景不是透明的,因此事实证明了我之前所说的。

更新结束


我有这样的MainActivity布局:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_height="match_parent">

    <!-- As the main content view, the view below consumes the entire
         space available using match_parent in both dimensions. -->
    <FrameLayout android:id="@+id/container" android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- android:layout_gravity="start" tells DrawerLayout to treat
         this as a sliding drawer on the left side for left-to-right
         languages and on the right side for right-to-left languages.
         If you're not building against API 17 or higher, use
         android:layout_gravity="left" instead. -->
    <!-- The drawer is given a fixed width in dp and extends the full height of
         the container. -->
    <fragment android:id="@+id/navigation_drawer"
        android:layout_width="@dimen/navigation_drawer_width" android:layout_height="match_parent"
        android:layout_gravity="start" tools:layout="@layout/fragment_navigation_drawer" />

</android.support.v4.widget.DrawerLayout>

ContentView我用来填充的片段@id/container配置为:

<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contentView"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
    android:id="@+id/tweet_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/grey_300"/>

</android.support.v4.widget.SwipeRefreshLayout>

这里是onCreateView()ContentView

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View inflatedView = inflater.inflate(R.layout.fragment_content, container, false);

    mRecyclerView = (RecyclerView) inflatedView.findViewById(R.id.tweet_list);
    mRecyclerView.setHasFixedSize(false);

    mLinearLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
    mRecyclerView.setLayoutManager(mLinearLayoutManager);

    mTweetList = new ArrayList<Tweet>;
    mAdapter = new TweetAdapter(mTweetList);
    mRecyclerView.setAdapter(mAdapter);
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());

    mSwipeRefreshLayout = (SwipeRefreshLayout) inflatedView.findViewById(R.id.contentView);
    mSwipeRefreshLayout.setOnRefreshListener(
        ...
    );
    return inflatedView;
}

然后MainActivity我通过切换不同内容来切换显示内容ContentView。除了一件事情,一切看起来都不错:当我ContentView在导航抽屉刷新期间切换片段时,内容冻结了。但是实际上所有事情都照常进行,除非您看不到它。


正常情况下,您需要刷新,而在更改整个列表时,&会在刷新终止时尝试重新加载已更改的数据。刷新时,您应该锁定滑动。
2014年

好吧,看来这是Google的错...升级到AppCompat v21.0.2后,所有的烦恼都已经消失在XD上了。无论如何还是要感谢<3
zlm2012

Answers:


105

好吧……经过一番挣扎之后,我终于以棘手的方式自己解决了这个问题。

我只需要在这些添加onPause()

@Override
public void onPause() {
    super.onPause();
    ...

    if (mSwipeRefreshLayout!=null) {
        mSwipeRefreshLayout.setRefreshing(false);
        mSwipeRefreshLayout.destroyDrawingCache();
        mSwipeRefreshLayout.clearAnimation();
    }
}

1
虽然我添加了此代码但似乎无法正常工作,我的支持库为22.0.0,实际上我发现冻结未在我的应用程序中消失,我尝试使用setTransition(),这也可以解决问题,但是这两个代码和setTransition()并不完全正确,如果您在滑动布局开始刷新后约一秒钟内替换片段,您还会发现冻结内容,但是一旦超过一小段时间,片段替换就可以正常工作,我的最后一个解决方法是翘曲框架布局中的滑动布局,您无需再添加其他代码。
cajsaiko

问题仍然存在support-v4:22.1.1,使用滑动刷新和滑动菜单时遇到了这个问题。我在调用片段事务时尝试将setRefreshing设置为false,但这没有用。感谢您的解决方案。
Ultimo_m 2015年

5
将SwipeRefreshLayout封装在Appcompat v7:22+中解决的FrameLayout中
Rahul Rastogi

1
也可以在这里工作:com.android.support:appcompat-v7:24.2.1
杰里米(Jeremy)

2
谢谢。我只setRefreshing(false)在FragmentTransaction期间有过。添加后clearAnimation(),我的应用程序不会冻结。
RobinBattle '16

76

这个问题似乎仍在appcompat 22.1.1中发生。将SwipeRefreshLayout包裹在FrameLayout中为我解决了这个问题

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.widget.SwipeRefreshLayout

    android:id="@+id/contentView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/tweet_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/grey_300" />

</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>

效果很好,谢谢!骇客程度较低,但效率可能比多数投票的解决方案稍微低一点?
2015年

1
嗨@Pin,我认为这不是比其他解决方案更重的解决方案。FrameLayout是最轻的ViewGroup元素,它不应增加太多复杂性。
andrea.rinaldi 2015年

1
实际上,这在“片段”中对我有用。哇。
EpicPandaForce

2
谢谢!解决了我的问题!我浪费了这么多时间来寻找原因和解决方案。必须接受它作为正确答案!
Trancer

3
在25.2.0中,仍然发生错误,此问题得以解决。在找到此解决方案之前,我想翻转一下桌子。谢谢。
卢卡斯·诺瓦克

3

除了@ zlm2012答案之外,我还注意到此问题已在支持库21.0.0中重现,但现在似乎已修复为21.0.3。


1
在22.2.1中,错误仍然发生。
现场

3
我在compile 'com.android.support:appcompat-v7:25.1.0'
EpicPandaForce

1

该解决方案有效,但我认为最好将这些行放入其自己的函数中,例如:

public void terminateRefreshing() {
    mSwipeRefreshLayout.setRefreshing(false);
    mSwipeRefreshLayout.destroyDrawingCache();
    mSwipeRefreshLayout.clearAnimation();
}

并在切换片段时调用。

Fragment prevFrag = fragmentManager.findFragmentById(drawerContainerId);

if(prevFrag instanceof SwipeRefreshFragment) {
    ((SwipeRefreshFragment)prevFrag).terminateRefreshing();
}

fragmentManager.beginTransaction().replace(drawerContainerId, fragment).commit();

1

有用!只需从片段替换中删除过渡,就我而言,我从代码中删除了以下内容:

.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)


0

目前,您可以通过以下方式避免此问题:

public class FixedRefreshLayout extends SwipeRefreshLayout {

    private static final String TAG = "RefreshTag";
    private boolean selfCancelled = false;

    public FixedRefreshLayout(Context context) {
        super(context);
    }

    public FixedRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected Parcelable onSaveInstanceState()
    {
        if(isRefreshing()) {
            clearAnimation();
            setRefreshing(false);
            selfCancelled = true;
            Log.d(TAG, "For hide refreshing");
        }
        return super.onSaveInstanceState();
    }

    @Override
    public void setRefreshing(boolean refreshing) {
        super.setRefreshing(refreshing);
        selfCancelled = false;
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        if(hasWindowFocus && selfCancelled) {
            setRefreshing(true);
            Log.d(TAG, "Force show refreshing");
        }
    }
}

如果您决定不切换片段(来自某些菜单),则还可以恢复动画。它还与内部动画相关联,以确保如果网络刷新已经完成,则刷新动画不会返回。


0

每当单击导航菜单项时,终止SwipeRefresh。有效。

private void selectItem(int position) {

       mSwipeRefreshLayout.setRefreshing(false);

       /*
        Other part of the function
       */
}

我这样做了,但我还必须按照已接受的答案进行操作,否则,从开始刷新起,我在屏幕上就会留下很多视图
EpicPandaForce
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.