Android AppBarLayout中的工具栏是可滚动的,尽管RecyclerView没有足够的内容可滚动
AppBarLayout中的工具栏确实是可滚动的,尽管具有“appbar\u滚动\u查看\u行为”的主容器没有足够的内容来真正滚动吗 到目前为止我测试的内容:Android AppBarLayout中的工具栏是可滚动的,尽管RecyclerView没有足够的内容可滚动,android,android-xml,android-coordinatorlayout,android-appbarlayout,Android,Android Xml,Android Coordinatorlayout,Android Appbarlayout,AppBarLayout中的工具栏确实是可滚动的,尽管具有“appbar\u滚动\u查看\u行为”的主容器没有足够的内容来真正滚动吗 到目前为止我测试的内容: 当我使用NestedScrollView(带有“wrap_content”属性)作为主容器,使用TextView作为子容器时,AppBarLayout工作正常,不会滚动 但是,当我使用只有几个条目的RecyclerView和“wrap_content”属性(这样就不需要滚动)时,AppBarLayout中的工具栏是可滚动的,即使Recyc
当我使用NestedScrollView(带有“wrap_content”属性)作为主容器,使用TextView作为子容器时,AppBarLayout工作正常,不会滚动 但是,当我使用只有几个条目的RecyclerView和“wrap_content”属性(这样就不需要滚动)时,AppBarLayout中的工具栏是可滚动的,即使RecyclerView从未收到滚动事件(使用OnScrollChangeListener测试) 这是我的布局代码:
<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/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:theme="@style/ToolbarStyle" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
编辑#2:
我刚刚玩了一个新的应用程序,这个(非故意的)行为似乎已经在支持库23.3.0版(甚至更早)中得到了修复。因此,不再需要变通方法了 在
工具栏中
删除滚动
标志,只保留enterally
标志,您应该会得到您想要的效果。为完整起见,您的布局应如下所示:
<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/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="enterAlways"
app:theme="@style/ToolbarStyle" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
我建议您尝试使用该示例来支持设计库元素
此布局与示例中的布局类似
<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/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
这不是一个bug,视图组中的所有事件都是以这种方式处理的。因为您的recyclerview是coordinatorLayout的子级,所以每当生成事件时,都会首先检查父级,如果父级不感兴趣,则只将其传递给子级。
请参见google在
LayoutManager
子类中类似的内容似乎会产生所需的行为:
@Override
public boolean canScrollVertically() {
int firstCompletelyVisibleItemPosition = findFirstCompletelyVisibleItemPosition();
if (firstCompletelyVisibleItemPosition == RecyclerView.NO_POSITION) return false;
int lastCompletelyVisibleItemPosition = findLastCompletelyVisibleItemPosition();
if (lastCompletelyVisibleItemPosition == RecyclerView.NO_POSITION) return false;
if (firstCompletelyVisibleItemPosition == 0 &&
lastCompletelyVisibleItemPosition == getItemCount() - 1)
return false;
return super.canScrollVertically();
}
final View appBarLayout = ((DrawerActivity) getActivity()).getAppBarLayoutView();
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
layoutParams.setBehavior(new AppBarLayoutNoEmptyScrollBehavior(recyclerView, getResources().getDimensionPixelSize(R.dimen.control_bar_height)));
canScrollVertical()
的文档说明:
/**
* Query if vertical scrolling is currently supported. The default implementation
* returns false.
*
* @return True if this LayoutManager can scroll the current contents vertically
*/
请注意“可以垂直滚动当前内容”,我认为这意味着当前状态应该由返回值反映出来
但是,通过v7 recyclerview库(23.1.1)提供的任何
LayoutManager
子类都没有做到这一点,这让我有点犹豫这是否是一个正确的解决方案;它可能会在本问题中讨论的情况以外的其他情况下产生不希望的效果。我使用自己的行为类实现了它,该类可能附加到AppBarLayout:
public class CustomAppBarLayoutBehavior extends AppBarLayout.Behavior {
private RecyclerView recyclerView;
private int additionalHeight;
public CustomAppBarLayoutBehavior(RecyclerView recyclerView, int additionalHeight) {
this.recyclerView = recyclerView;
this.additionalHeight = additionalHeight;
}
public boolean isRecyclerViewScrollable(RecyclerView recyclerView) {
return recyclerView.computeHorizontalScrollRange() > recyclerView.getWidth() || recyclerView.computeVerticalScrollRange() > (recyclerView.getHeight() - additionalHeight);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
if (isRecyclerViewScrollable(mRecyclerView)) {
return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
}
return false;
}
}
下面是如何设置此行为的代码:
@Override
public boolean canScrollVertically() {
int firstCompletelyVisibleItemPosition = findFirstCompletelyVisibleItemPosition();
if (firstCompletelyVisibleItemPosition == RecyclerView.NO_POSITION) return false;
int lastCompletelyVisibleItemPosition = findLastCompletelyVisibleItemPosition();
if (lastCompletelyVisibleItemPosition == RecyclerView.NO_POSITION) return false;
if (firstCompletelyVisibleItemPosition == 0 &&
lastCompletelyVisibleItemPosition == getItemCount() - 1)
return false;
return super.canScrollVertically();
}
final View appBarLayout = ((DrawerActivity) getActivity()).getAppBarLayoutView();
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
layoutParams.setBehavior(new AppBarLayoutNoEmptyScrollBehavior(recyclerView, getResources().getDimensionPixelSize(R.dimen.control_bar_height)));
编辑2: 当RecyclerView不可滚动时,确保工具栏不可滚动的唯一方法是以编程方式设置setScrollFlags,这需要检查RecyclerView是否可滚动。每次修改适配器时都必须进行此检查 与活动进行沟通的接口:
公共接口布局控制器{
void enableColl();
void disableScroll();
}
主要活动:
public类MainActivity扩展了AppCompatActivity实现
布局控制器{
私有折叠工具栏布局折叠工具栏布局;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar Toolbar=(Toolbar)findViewById(R.id.Toolbar);
设置支持操作栏(工具栏);
折叠工具栏布局=
(折叠工具栏布局)findViewById(R.id.折叠工具栏);
final FragmentManager=getSupportFragmentManager();
最终片段片段=新CheeseListFragment();
经理:开始营业()
.replace(R.id.root\u内容,片段)
.commit();
}
@凌驾
public void enableScroll(){
最终AppBarLayout.LayoutParams参数=(AppBarLayout.LayoutParams)
塌陷ToolbarLayout.getLayoutParams();
参数设置CrollFlags(
AppBarLayout.LayoutParams.SCROLL\u FLAG\u SCROLL
|AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
);
折叠ToolbarLayout.setLayoutParams(参数);
}
@凌驾
public void disableScroll(){
最终AppBarLayout.LayoutParams参数=(AppBarLayout.LayoutParams)
塌陷ToolbarLayout.getLayoutParams();
参数setScrollFlags(0);
折叠ToolbarLayout.setLayoutParams(参数);
}
}
activity_main.xml:
编辑:
您应该折叠工具栏布局以控制行为
通过将工具栏直接添加到AppBarLayout,可以访问enterAlwaysCollapsed和exitUntilCollapsed滚动标志,但不能详细控制不同元素对折叠的反应。
[…]安装程序使用CollavingToolbarLayout的app:layout\u collapseMode=“pin”确保在视图折叠时工具栏本身固定在屏幕顶部
加
app:layout\u collapseMode=“pin”
以xml格式创建工具栏
所以,正确的回答几乎解决了我的问题。但是,由于当你有一个可滚动的recyclerview时,它没有显示工具栏,并且它的最后一项是可见的(在第一次向上滚动时它不会显示工具栏),我决定对它进行修改,并对其进行调整,以便于实现和动态适配器
首先,必须为appbar创建自定义布局行为:
public class ToolbarBehavior extends AppBarLayout.Behavior{
private boolean scrollableRecyclerView = false;
private int count;
public ToolbarBehavior() {
}
public ToolbarBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
return scrollableRecyclerView && super.onInterceptTouchEvent(parent, child, ev);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {
updatedScrollable(directTargetChild);
return scrollableRecyclerView && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
return scrollableRecyclerView && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
private void updatedScrollable(View directTargetChild) {
if (directTargetChild instanceof RecyclerView) {
RecyclerView recyclerView = (RecyclerView) directTargetChild;
RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (adapter != null) {
if (adapter.getItemCount()!= count) {
scrollableRecyclerView = false;
count = adapter.getItemCount();
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager != null) {
int lastVisibleItem = 0;
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
lastVisibleItem = Math.abs(linearLayoutManager.findLastCompletelyVisibleItemPosition());
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
int[] lastItems = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(new int[staggeredGridLayoutManager.getSpanCount()]);
lastVisibleItem = Math.abs(lastItems[lastItems.length - 1]);
}
scrollableRecyclerView = lastVisibleItem < count - 1;
}
}
}
} else scrollableRecyclerView = true;
}
}
公共类工具栏行为扩展了AppBarLayout.Behavior{
私有布尔scrollableRecyclerView=false;
私人整数计数;
公共行为{
}
公共工具栏行为(上下文、属性集属性){
超级(上下文,attrs);
}
@凌驾
公共布尔值onInterceptTouchEvent(CoordinatorLayout父级、AppBarLayout子级、MotionEvent ev){
返回sc
public class RecyclerViewCustom extends RecyclerView implements ViewTreeObserver.OnGlobalLayoutListener
{
public RecyclerViewCustom(Context context)
{
super(context);
}
public RecyclerViewCustom(Context context, @Nullable AttributeSet attrs)
{
super(context, attrs);
}
public RecyclerViewCustom(Context context, @Nullable AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
/**
* This supports scrolling when using RecyclerView with AppbarLayout
* Basically RecyclerView should not be scrollable when there's no data or the last item is visible
*
* Call this method after Adapter#updateData() get called
*/
public void addOnGlobalLayoutListener()
{
this.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout()
{
// If the last item is visible or there's no data, the RecyclerView should not be scrollable
RecyclerView.LayoutManager layoutManager = getLayoutManager();
final RecyclerView.Adapter adapter = getAdapter();
if (adapter == null || adapter.getItemCount() <= 0 || layoutManager == null)
{
setNestedScrollingEnabled(false);
}
else
{
int lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();
boolean isLastItemVisible = lastVisibleItemPosition == adapter.getItemCount() - 1;
setNestedScrollingEnabled(!isLastItemVisible);
}
unregisterGlobalLayoutListener();
}
private void unregisterGlobalLayoutListener()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
else
{
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
}
// Find out if RecyclerView are scrollable, delay required
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (rv.canScrollVertically(DOWN) || rv.canScrollVertically(UP)) {
controller.enableScroll();
} else {
controller.disableScroll();
}
}
}, 100);
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if(bottom != oldBottom)
{
mActivity.setScrollEnabled(view.canScrollVertically(0) || view.canScrollVertically(1));
}
}
});
public void setScrollEnabled(boolean enabled) {
final AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams)
mToolbar.getLayoutParams();
params.setScrollFlags(enabled ?
AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS : 0);
mToolbar.setLayoutParams(params);
}