【问题标题】:Android: CollapsingToolbarLayout and SwipeRefreshLayout get stuckAndroid:CollapsingToolbarLayout 和 SwipeRefreshLayout 卡住
【发布时间】:2015-08-27 01:58:30
【问题描述】:

我同时使用 CollapsingToolbarLayout、RecyclerView 和 SwipeRefreshLayout:

Xml:

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

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/collapse_toolbar_height"
    android:fitsSystemWindows="true"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:contentScrim="?attr/colorPrimary"
        android:fitsSystemWindows="true"
        app:expandedTitleMarginStart="48dp"
        app:expandedTitleMarginEnd="64dp"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

        <ImageView
            android:id="@+id/toolbar_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            android:fitsSystemWindows="true"
            app:layout_collapseMode="parallax" />

        <include
            layout="@layout/activity_main_toolbar"/>

    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <cz.yetanotherview.webcamviewer.app.helper.EmptyRecyclerView
            android:id="@+id/mainList"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />
    </android.support.v4.widget.SwipeRefreshLayout>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/floating_action_button"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|right|end"
        android:layout_margin="16dp"
        app:fabSize="mini"
        android:src="@drawable/ic_action_edit"
        android:onClick="assignSelectedWebCamsToCategory"/>

    <com.github.clans.fab.FloatingActionMenu
        android:id="@+id/floating_action_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="bottom|end"
        android:paddingRight="10dp"
        android:paddingBottom="8dp"
        android:paddingLeft="10dp"
        fab:menu_shadowColor="#37000000"
        fab:menu_colorNormal="#DA4336"
        fab:menu_colorPressed="#E75043"
        fab:menu_colorRipple="#99FFFFFF"
        fab:menu_icon="@drawable/fab_add"
        fab:menu_buttonSpacing="10dp"
        fab:menu_labels_textColor="@color/very_dark_grey"
        fab:menu_labels_textSize="14sp"
        fab:menu_labels_colorNormal="@color/white"
        fab:menu_labels_colorPressed="@color/next_grey"
        fab:menu_labels_colorRipple="#99FFFFFF"
        fab:menu_labels_margin="8dp"
        fab:menu_backgroundColor="@color/black_transparent">

        <com.github.clans.fab.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_action_content_import"
            fab:fab_size="mini"
            fab:fab_label="@string/pref_import_from_server"
            fab:fab_colorNormal="@color/white"
            app:fab_colorPressed="@color/next_grey"
            app:fab_colorRipple="#99FFFFFF"
            android:onClick="showSelectionDialog"/>

        <com.github.clans.fab.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_action_content_manually"
            fab:fab_size="mini"
            fab:fab_label="@string/create_manually"
            fab:fab_colorNormal="@color/white"
            app:fab_colorPressed="@color/next_grey"
            app:fab_colorRipple="#99FFFFFF"
            android:onClick="showAddDialog"/>

        <com.github.clans.fab.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_action_content_suggestion"
            fab:fab_size="mini"
            fab:fab_label="@string/submit_suggestion"
            fab:fab_colorNormal="@color/white"
            app:fab_colorPressed="@color/next_grey"
            app:fab_colorRipple="#99FFFFFF"
            android:onClick="showSuggestionDialog"/>

    </com.github.clans.fab.FloatingActionMenu>
</android.support.design.widget.CoordinatorLayout>

<include
    layout="@layout/activity_main_drawer"/>
</android.support.v4.widget.DrawerLayout>

代码:

    swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
    swipeRefreshLayout.setOnRefreshListener(this);

当折叠工具栏布局完全展开并且滚动视图(recyclerview)在顶部时,如何允许滑动刷新操作? Google+ 或 Inbox 应用程序中的类似行为。

错误:

好:

【问题讨论】:

    标签: android swiperefreshlayout android-collapsingtoolbarlayout


    【解决方案1】:

    更新:此问题已在最新版本的支持库 (23.1.1+) 中得到解决。如果您使用的是旧版本的支持库,请升级或继续阅读。

    如果您使用的是旧版本的支持库,请向您的 AppBarLayout 添加偏移量更改侦听器,以相应地启用或禁用滑动以刷新布局。此处提供其他代码:

    https://gist.github.com/blackcj/001a90c7775765ad5212

    相关变化:

    public class MainActivity extends AppCompatActivity implements AppBarLayout.OnOffsetChangedListener {
        ...
    
        private AppBarLayout appBarLayout;
        private SwipeRefreshLayout mSwipeRefreshLayout;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            ...
            mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.contentView);
            appBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
    
        }
    
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
            //The Refresh must be only active when the offset is zero :
            mSwipeRefreshLayout.setEnabled(i == 0);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            appBarLayout.addOnOffsetChangedListener(this);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            appBarLayout.removeOnOffsetChangedListener(this);
        }
    }
    

    【讨论】:

    • 如果SwipeRefreshLayout 位于Fragment 中,而ViewPager 则位于ViewPager 中?
    • @AndyRoid 我在 GitHub 上更新了我的设计支持示例,以包含此代码。除了监听 onOffsetChanged 之外,您还需要覆盖 Activity 的 dispatchTouchEvent。然后在Fragment 中包含一个公共函数来启用/禁用SwipeRefreshLayout。此处提供完整代码:github.com/blackcj/DesignSupportExample
    • 干得好 blackcj 我会检查一下,看看是否一切正常!编辑:根据我在您的回购中看到的示例,到目前为止看起来很清楚,尚未在设备上对其进行测试。会及时通知您。
    • 请注意,这不适用于支持库 v23.0.0 和 v23.0.1,因为如果嵌套滚动已经开始,则嵌套滚动功能会忽略启用状态。如果您在一个动作中向下和向上滚动,无论如何它都会触发刷新,并且 AppBarLayout 保持折叠状态。
    • 这是个好主意,但需要一些修复。如果它开始完全展开(启用 SwipeRefresh)并且我触摸开始滚动,它将保持启用状态,直到我释放触摸。因此,如果我向下滚动并再次向上滚动,它会在完全展开之前尝试刷新。
    【解决方案2】:

    最后,

    我发现从支持库版本 23.1.1 开始,SwipeRefreshLayout 的工作原理没有任何“黑客”

    只需在您的布局中使用:

     <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scrollbars="vertical" />
    </android.support.v4.widget.SwipeRefreshLayout>
    

    在代码中:

    SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
    swipeRefreshLayout.setColorSchemeResources(R.color.green, R.color.red, R.color.yellow);
    swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
           //Your refresh code here
        }
    });
    

    别忘了使用:

    swipeRefreshLayout.setRefreshing(false);
    

    在使用你的代码逻辑之后;)

    【讨论】:

    • 使用新的支持库 23.2.0 又是问题:code.google.com/p/android/issues/detail?id=201775
    • 如果 SwipeRefreshLayout 不在 CoordinatorLayout 内怎么办?我有一个使用 SwipeRefreshLayout 的 ViewPager
    • 好的,有了支持库 23.3.0 再次一切正常且运行良好;)
    • 对于任何为此苦苦挣扎的人,我只想指出app:layout_behavior="@string/appbar_scrolling_view_behavior" 已在SwipeRefreshLayout 上。我在我的可滚动视图上有这个,而不是 SwipeRefreshLayout 并且没有任何工作。
    【解决方案3】:

    如果我理解正确,您只想在工具栏展开后开始刷新,对吗?所以首先需要打开 CollapsingToolbarLayout 然后开始刷新。我通过以下代码管理它:

         <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/coordinator_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true">
    
            <android.support.design.widget.AppBarLayout
                android:id="@+id/app_bar_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fitsSystemWindows="true"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    
                <android.support.design.widget.CollapsingToolbarLayout
                    android:id="@+id/collapsing_toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:fitsSystemWindows="true"
                    app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed">
    
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical">
                          <!--PUT HERE WHAT EVER YOU WANT TO COLLAPSE, A TOOLBAR, ETC...-->
                    </LinearLayout>
                </android.support.design.widget.CollapsingToolbarLayout>
            </android.support.design.widget.AppBarLayout>
         <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recycler_view"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:clipToPadding="false"
                    android:fadeScrollbars="false"
                    android:scrollbars="vertical"
                    app:layout_behavior="@string/appbar_scrolling_view_behavior" />
        </android.support.v4.widget.SwipeRefreshLayout>
    </android.support.design.widget.CoordinatorLayout>   
    

    然后,在您的片段/活动中使其实现 AppBarLayout.OnOffsetChangedListener (现在当工具栏完全展开时启用刷新):

        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            if (collapsingToolbarLayout.getHeight() + verticalOffset < 2 * ViewCompat.getMinimumHeight(collapsingToolbarLayout)) {
                swipeRefreshLayout.setEnabled(false);
            } else {
                swipeRefreshLayout.setEnabled(true);
            }
        }
    

    覆盖 onPause() & onResume() 如@blackcj 回答:

      @Override
        public void onResume() {
            super.onResume();
            appBarLayout.addOnOffsetChangedListener(this);
        }
    
        @Override
        public void onPause() {
            super.onPause();
            appBarLayout.removeOnOffsetChangedListener(this);
        }
    

    然后将 LinearLayoutManager 设置为你的 recyclerView:

        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
    

    对我来说,这很有魅力,首先 appBarlayout 得到扩展,然后 swipeRefreshLayout 才会触发刷新。

    【讨论】:

    • 需要将片段添加到嵌套滚动视图中。我很难过。
    • @user1232726 嗯..有什么问题?如果您将 FrameLayout 放在 NestedScrollView 中,然后从您的代码开始事务以将您的片段放入 framelayout 它应该可以工作,不是吗?
    • 您好,我尝试将 framelayout 放在 nestedScrollview 中,并尝试将其替换为包含 swiperefreshlayout 和 recycleView 的片段,但不起作用,recycleView 不显示。 (去掉swipeRefreshlayout,recycleview正常显示)
    • @LêKhánhVinh,试着把 android:fillViewport="true" 放到nestedScrollView
    • recyclerView 是一个nestedScrollView...你不需要两者。
    【解决方案4】:

    我必须将 RecyclerView 设为 SwipeRefreshLayout 的主要子项,以便使用 Support Library 23.2.0 消除该问题。无法修复它在 SwipeRefreshLayout 内具有 include 布局

    <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/my_swipeRefreshLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <!--<include layout="@layout/my_RecyclerView_layout"/> issue for me here -->
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/my_recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
            </android.support.v7.widget.RecyclerView>
    
        </android.support.v4.widget.SwipeRefreshLayout>
    

    【讨论】:

      【解决方案5】:

      对我来说,它与 offsetChangedListener 一起工作,只是性能更好的 kotlin onOffsetChanged() 实现

      val toEnable = verticalOffset == 0
      if (!swipeRefresh.isEnabled.xor(toEnable)) return
      swipeRefresh.isEnabled = toEnable
      

      【讨论】:

        【解决方案6】:

        Above Answer 非常适合AppCompatActivity,但如果您使用的是Fragment,那么关注 sn-p 将对您有所帮助。

        只需将NestedScrollView 放入fragment 的xml 中

        <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="fill_vertical"
            android:clipToPadding="false"
            android:isScrollContainer="false"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
        
            <!-- A RecyclerView with some commonly used attributes -->
                <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:id="@+id/album_timeline_swipe_refresh_layout"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
        
                    <android.support.v7.widget.RecyclerView
                        android:id="@+id/album_timeline_recyclerview"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:clickable="true" />
                </android.support.v4.widget.SwipeRefreshLayout>
        
        </android.support.v4.widget.NestedScrollView>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-23
          • 2015-01-28
          • 1970-01-01
          • 1970-01-01
          • 2014-09-30
          • 2015-12-21
          相关资源
          最近更新 更多