我有一个相当典型的列表功能,使用 CoordinatorLayout、AppBarLayout、SwipeRefreshLayout 和 RecyclerView -
当 RecyclerView 有足够的内容可以滚动时,页面看起来不错。然而,当 RecyclerView 为空或没有足够的内容可供滚动时,行为是具有
app:layout_scrollFlags="scroll|enterAlwaysCollapsed"
的 AppBarLayout 子级将继续滚动 - 这看起来很奇怪。
有没有办法在 NestedScrollView 为空时停止 AppBarLayout 子级滚动?
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:elevation="4dp">
<LinearLayout
android:id="@+id/eventHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/green"
android:orientation="horizontal"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="scroll|enterAlwaysCollapsed"
android:textColor="@color/white"
android:textSize="15sp"/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeToRefresh"
android:layout_width="match_parent"
android:layout_gravity="fill_vertical"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:dividerHeight="0dp"
android:layout_gravity="fill_vertical"
android:drawSelectorOnTop="true"
android:listSelector="@drawable/selector_ripple_grey_transparent"
android:scrollbars="vertical"/>
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>
<TextView
android:id="@+id/noData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="16dp"
android:text="@string/no_data_available"
android:textSize="17sp"/>
</FrameLayout>
不确定这是多么优雅的解决方案,但是,我重写了
onStartNestedScroll()
事件,仅在 NestedScrollView 可滚动时触发(在本例中为 RecyclerView)
在onCreate()中:
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
layoutParams.setBehavior(new AppBarLayoutNoEmptyScrollBehavior(mAppBarLayout, mRecyclerView));
行为:
public class AppBarLayoutNoEmptyScrollBehavior extends AppBarLayout.Behavior {
AppBarLayout mAppBarLayout;
RecyclerView mRecyclerView;
public AppBarLayoutNoEmptyScrollBehavior(AppBarLayout appBarLayout, RecyclerView recyclerView) {
mAppBarLayout = appBarLayout;
mRecyclerView = recyclerView;
}
public boolean isRecylerViewScrollable(RecyclerView recyclerView) {
int recyclerViewHeight = recyclerView.getHeight(); // Height includes RecyclerView plus AppBarLayout at same level
int appCompatHeight = mAppBarLayout != null ? mAppBarLayout.getHeight() : 0;
recyclerViewHeight -= appCompatHeight;
return recyclerView.computeVerticalScrollRange() > recyclerViewHeight;
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
if (isRecylerViewScrollable(mRecyclerView)) {
return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
}
return false;
}
}
编辑
编辑后的解决方案,RecyclerView 给出的高度为 visible RecyclerView 高度和 AppBarLayout 高度(即 CoordinatorLayout 高度)。
但是,如果您的滚动手势在可见的 AppBarLayout 区域上开始,即使您也将此 Behaviour 添加到 AppBarLayout 中,仍然会发生滚动。因此,这个答案并不能解决问题。
创建此类。
public class AppBarLayoutBehaviorForEmptyRecyclerView extends AppBarLayout.Behavior
{
private boolean canRecyclerViewBeScrolled = false;
public AppBarLayoutBehaviorForEmptyRecyclerView()
{
}
public AppBarLayoutBehaviorForEmptyRecyclerView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev)
{
return canRecyclerViewBeScrolled && super.onInterceptTouchEvent(parent, child, ev);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes)
{
updateScrollable(target);
return canRecyclerViewBeScrolled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed)
{
return canRecyclerViewBeScrolled && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
private void updateScrollable(View targetChild)
{
if(targetChild instanceof RecyclerView)
{
RecyclerView.Adapter adapter = ((RecyclerView) targetChild).getAdapter();
canRecyclerViewBeScrolled = adapter != null && adapter.getItemCount() > 0;
}
else
{
canRecyclerViewBeScrolled = true;
}
}
}
将以下属性添加到您的 AppBarLayout
XML 元素:
app:layout_behavior="com.xxxx.xxxxxx.AppBarLayoutBehaviorForEmptyRecyclerView"
答案是好的,但我还在构造函数中添加了
public AppBarLayoutOnEmptyRecyclerViewScrollBehavior(@NonNull AppBarLayout appBarLayout, @NonNull RecyclerView recyclerView) {
this.appBarLayout = checkNotNull(appBarLayout);
this.recyclerView = checkNotNull(recyclerView);
setDragCallback(new DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
return isRecylerViewScrollable(recyclerView);
}
});
}
所以当
RecyclerView
为空时,我也禁用从
AppBarLayout
的拖动
AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mEventHeader.getLayoutParams();
toolbarLayoutParams.setScrollFlags(0);
mEventHeader.setLayoutParams(toolbarLayoutParams);
如果不为空,则设置回滚动标志:
AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mEventHeader.getLayoutParams();
toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
mEventHeader.setLayoutParams(toolbarLayoutParams);
,Kostek。 谢谢你们! 我的解决方案结合了它们+它是
Kotlin实现并修复了已弃用的代码。 创建班级
AppBarLayoutNoNeedScrollBehavior
:
/**
* Behavior to skip scrolling (hide) [AppBarLayout] if [RecyclerView] not scrollable.
*/
class AppBarLayoutNoNeedScrollBehavior(
private val appBarLayout: AppBarLayout,
private val recyclerView: RecyclerView
) : AppBarLayout.Behavior() {
init {
setDragCallback(object : DragCallback() {
override fun canDrag(appBarLayout: AppBarLayout): Boolean = isScrollable()
})
}
fun isScrollable(): Boolean {
/** Check [recyclerView] has some content. */
recyclerView.adapter?.itemCount?.takeIf { it > 0 } ?: return false
/** [recyclerView] height includes [appBarLayout], because we must exclude it. */
var recyclerHeight = recyclerView.height - appBarLayout.height
/** In case if we have some padding at the bottom - it should be excluded too. */
recyclerHeight -= recyclerView.paddingBottom
return recyclerView.computeVerticalScrollRange() > recyclerHeight
}
override fun onStartNestedScroll(
parent: CoordinatorLayout,
child: AppBarLayout,
directTargetChild: View,
target: View,
nestedScrollAxes: Int,
type: Int
): Boolean {
/** If [recyclerView] not scrollable - don't allow to scroll. */
if (!isScrollable()) return false
return super.onStartNestedScroll(
parent, child, directTargetChild, target, nestedScrollAxes, type
)
}
}
在您的代码中:
val appBarLayout = ..
val recyclerView = ..
val layoutParams = appBarLayout.layoutParams as? CoordinatorLayout.LayoutParams
layoutParams?.behavior = AppBarLayoutNoNeedScrollBehavior(appBarLayout, recyclerView)