Android:CollapsingToolbarLayout 和 SwipeRefreshLayout 卡住
Posted
技术标签:
【中文标题】Android:CollapsingToolbarLayout 和 SwipeRefreshLayout 卡住【英文标题】:Android: CollapsingToolbarLayout and SwipeRefreshLayout get stuck 【发布时间】: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_
android:layout_
android:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinator_layout"
android:layout_
android:layout_
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_
android:layout_
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_
android:layout_
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_
android:layout_
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_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<cz.yetanotherview.webcamviewer.app.helper.EmptyRecyclerView
android:id="@+id/mainList"
android:layout_
android:layout_
android:scrollbars="vertical" />
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/floating_action_button"
android:layout_
android:layout_
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_
android:layout_
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_
android:layout_
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_
android:layout_
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_
android:layout_
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 应用程序中的类似行为。
错误:
好:
【问题讨论】:
【参考方案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_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_
android:layout_
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 23.2 解决方法:***.com/questions/35641630/… 如果 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_
android:layout_
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_
android:layout_
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_
android:layout_
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed">
<LinearLayout
android:layout_
android:layout_
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_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_
android:layout_
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_
android:layout_>
<!--<include layout="@layout/my_RecyclerView_layout"/> issue for me here -->
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recyclerView"
android:layout_
android:layout_>
</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_
android:layout_
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_
android:layout_>
<android.support.v7.widget.RecyclerView
android:id="@+id/album_timeline_recyclerview"
android:layout_
android:layout_
android:clickable="true" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.v4.widget.NestedScrollView>
【讨论】:
以上是关于Android:CollapsingToolbarLayout 和 SwipeRefreshLayout 卡住的主要内容,如果未能解决你的问题,请参考以下文章
CollapsingToolbar, Viewpager 有 recyclerview 和 NestedScrollview
使用 CollapsingToolbar 滚动时如何更新工具栏