如何禁用 NestedScrollView&CollapsingToolbarLayout 的滚动,例如当下面没有更多内容时?
Posted
技术标签:
【中文标题】如何禁用 NestedScrollView&CollapsingToolbarLayout 的滚动,例如当下面没有更多内容时?【英文标题】:How to disable scrolling of NestedScrollView&CollapsingToolbarLayout, for example when there is no more content below? 【发布时间】:2016-02-11 09:36:05 【问题描述】:背景
我尝试添加与许多应用程序相同的功能,其中屏幕的上部区域根据滚动的内容缩小和扩大。
为此,我使用 Google 的设计库,如 the CheeseSquare sample 所示。
问题
问题是,无论 NestedScrollView 中有多少内容,它都可以让我滚动到内容的最后一个视图下方,只是为了让我看到操作栏的最终状态,它本身的大小是最小的。
简而言之,这是我滚动到底部时看到的(CheeseSquare 示例的修改内容):
虽然这是我在滚动到底部时想要的(取自联系人应用程序):
我还在尝试修复ThreePhasesBottomSheet 示例中的一个错误,即即使处于窥视状态,也可以在底部工作表内容中滚动。要重现,开始水平滚动(这不会做任何事情,因为没有任何东西可以这样滚动)然后垂直滚动,这会以某种方式触发底页内容的滚动。
因此,我需要在“transformView()”方法中禁用滚动,以防“翻译
这是正常使用时的工作方式:
这就是它在不阻止滚动的错误时的行为方式:
我尝试过的
我尝试使用“layout_scrollFlags”标志,将高度更改为 wrap_content,并删除 clipToPadding 和 fitSystemWindows 属性。
这是示例 XML 文件,我已对其进行了修改,使其仅包含一个 cardView 而不是多个:
<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_
android:layout_
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_
android:layout_
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_
android:layout_
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp">
<ImageView
android:id="@+id/backdrop"
android:layout_
android:layout_
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_
android:layout_
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_
android:layout_
android:orientation="vertical"
android:paddingTop="24dp">
<android.support.v7.widget.CardView
android:layout_
android:layout_
android:layout_margin="@dimen/card_margin">
<LinearLayout
style="@style/Widget.CardContent"
android:layout_
android:layout_>
<TextView
android:layout_
android:layout_
android:text="Info"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_
android:layout_
android:text="@string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_
android:layout_
app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|right|end"
android:src="@drawable/ic_discuss"
android:layout_margin="@dimen/fab_margin"
android:clickable="true"/>
</android.support.design.widget.CoordinatorLayout>
我也试过下一个代码:
((AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams()).setScrollFlags(0);
但这仍然允许在 CheeseSquare 示例中滚动 NestedScrollView 本身,并且还允许在 ThreePhasesBottomSheet 示例中滚动。
问题
当底部没有更多内容可显示时,我该怎么做才能使滚动停止?
此外,我可以在任何时候禁用 NestedScrollView 的滚动(对于 ThreePhasesBottomSheet 示例)?像“setEnableScrolling(...)”这样的东西?
我尝试扩展 NestedScrollView 并从 ScrollingViewBehavior 扩展,但未能找到禁用滚动的方法。
改起来大概是个很简单的事情,但是我不知道是什么...
编辑:如果需要,这是我目前用于设计和支持库的内容
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:design:23.1.0'
编辑:对于#2,我从 BottomSheetLayout.java 文件中找到了一种解决方法,以禁用与变量“sheetViewOwnsTouch”相关的所有内容,就好像它始终设置为“false”一样。这将允许窃取底部工作表上的触摸事件。但是,这只是一种解决方法,并且仅适用于这种情况。它还会导致一些应该由其他视图处理的触摸事件。我仍然想知道如何以编程方式阻止滚动,以及在足够空间显示内容的另一种情况下。
【问题讨论】:
不知道,但请提供您使用的库版本,以及您如何实现视图(滚动标志等) 我只更改了布局的内容(使用 textViews 而不是 cardViews)来显示这些屏幕截图。我已经更新了问题以显示库版本。为什么要对此投反对票? 抱歉,我认为这不是我对您的问题投反对票的原因。反正我去看看 @TunaKarakasoglu 很有趣。你能示范一下吗? @Virus 首先将屏幕捕获为视频文件(使用 ADB 或 Android Studio),然后使用您希望使用的任何工具将其转换为 gif,例如:image.online-convert.com/convert-to-gif 【参考方案1】:如果没有,我该怎么做才能使滚动停止 底部显示更多内容?
首先,正如我在下面评论的那样,您在问题中所说的滚动不是NestedScrollView
。它属于CollapsingToolbarLayout
。 NestedScrollView
的滚动事件仅在 CollapsingToolbarLayout
完全折叠时发生,当然当其中没有更多内容时它会停止滚动(到达底部)。对于CollapsingToolbarLayout
,它将折叠到其工具栏的layout_height(如在xml 文件中,您将找到"?attr/actionBarSize"
)。下图将演示,注意红色矩形是工具栏(我设置了它的背景)
所以要为您的#1 找到解决方案,您需要计算NestedScrollView
的高度,然后如果它小于屏幕高度,我们会修复工具栏的高度。
简而言之,您可以将activity_detail.xml
更新为:
<?xml version="1.0" encoding="utf-8"?>
<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_
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_
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/backdrop"
android:layout_
android:layout_
android:fitsSystemWindows="false"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_
android:layout_
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_
android:layout_
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_
android:layout_
android:layout_margin="@dimen/card_margin">
<LinearLayout
style="@style/Widget.CardContent"
android:layout_
android:layout_>
<TextView
android:layout_
android:layout_
android:text="Info"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:layout_
android:layout_
android:text="@string/cheese_ipsum" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
还有 CheeseDetailActivity.java:
public class CheeseDetailActivity extends AppCompatActivity
public static final String EXTRA_NAME = "cheese_name";
private final Context mContext = this;
private int screenHeight;
private int linearLayoutHeight;
private int toolbarHeight_org;
private int toolbarHeight;
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
Intent intent = getIntent();
final String cheeseName = intent.getStringExtra(EXTRA_NAME);
screenHeight = getScreenHeight(this);
TypedValue typedValue = new TypedValue();
getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
final int colorPrimary = typedValue.data;
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
AppBarLayout appbar = (AppBarLayout) findViewById(R.id.appbar);
final CoordinatorLayout.LayoutParams appbarLayoutParams = (CoordinatorLayout.LayoutParams)appbar.getLayoutParams();
final ViewGroup.LayoutParams toolbarLayoutParams = toolbar.getLayoutParams();
if (toolbarLayoutParams != null)
toolbarHeight_org = toolbarLayoutParams.height;
toolbarHeight = toolbarLayoutParams.height;
final CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle(cheeseName);
collapsingToolbar.setContentScrimColor(colorPrimary);
collapsingToolbar.setExpandedTitleTextAppearance(R.style.ExpandedTitleTextAppearance);
//collapsingToolbar.setCollapsedTitleTextAppearance(R.style.CollapsedTitleTextAppearance);
final LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linearLayout1);
ViewTreeObserver observer = linearLayout.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
@Override
public void onGlobalLayout()
linearLayoutHeight = linearLayout.getHeight();
if (linearLayoutHeight + toolbarHeight < screenHeight)
if (toolbarLayoutParams != null)
toolbarLayoutParams.height = screenHeight - linearLayoutHeight - 20;
if (toolbarLayoutParams.height < toolbarHeight_org)
toolbarLayoutParams.height = toolbarHeight_org;
int extended_text_size = (int) getResources().getDimension(R.dimen.expanded_text_size);
if (appbarLayoutParams.height - toolbarLayoutParams.height <= extended_text_size)
int value = appbarLayoutParams.height - toolbarLayoutParams.height;
if (value < 0)
appbarLayoutParams.height = toolbarLayoutParams.height - value + extended_text_size * 3;
else
appbarLayoutParams.height = toolbarLayoutParams.height + extended_text_size * 3;
if (appbarLayoutParams.height >= screenHeight)
appbarLayoutParams.height = screenHeight;
// collapsingToolbar.setContentScrimColor(getResources().getColor(android.R.color.transparent));
if (toolbarLayoutParams.height > toolbarHeight_org)
collapsingToolbar.setContentScrimColor(ContextCompat.getColor(mContext, android.R.color.transparent));
// Removes the listener if possible
ViewTreeObserver viewTreeObserver = linearLayout.getViewTreeObserver();
if (viewTreeObserver.isAlive())
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
linearLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
else
linearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
);
loadBackdrop();
appbar.setExpanded(true);
private int getScreenHeight(Context context)
int measuredHeight;
Point size = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
wm.getDefaultDisplay().getSize(size);
measuredHeight = size.y;
else
Display d = wm.getDefaultDisplay();
measuredHeight = d.getHeight();
return measuredHeight;
private void loadBackdrop()
final ImageView imageView = (ImageView) findViewById(R.id.backdrop);
Glide.with(this).load(Cheeses.getRandomCheeseDrawable()).centerCrop().into(imageView);
@Override
public boolean onCreateOptionsMenu(Menu menu)
getMenuInflater().inflate(R.menu.sample_actions, menu);
return true;
结果如下:
通过Cheesesquare示例,我已经定制了这个项目并上传到My GitHub。我同意它仍然存在一些问题,但是,至少它可以解决您的第一个问题。
请看一看。希望对您有所帮助!
【讨论】:
根据您展示的动画,这效果不佳,因为工具栏没有固定,标题的文本也没有移动和调整大小。这类似于只放置一个带有内容的滚动视图...... 你能解释一下你做了什么,你在哪里阻止滚动?它对我提供的第二个样品(三相底板)也有帮助吗? 正如你在我的代码中看到的,我没有对 NestedScrollView 做任何事情。实际上,NSV 的滚动事件只有在 Toolbar 固定到 actionbar 或 Toolbar 的 layout_height 固定时才会发生。 CollapsingToolbarLayout 依赖于 Toolbar 的 layout_height,所以我计算了 linearLayout1 的运行时高度,然后在 IF 中设置 Toolbar 的 layout_height。我不确定这是否适用于#2。 linearLayout1的高度当然和NestedScrollView有关(对不起我的英文,我没有太多话可以解释清楚):) @BNK 为什么我应该在我的 #1 案例中更改它?我不需要更改工具栏的大小。当底部没有更多内容可滚动时,我想停止滚动。【参考方案2】:要禁用滚动,只需将 NestedScrollView 和它的 LinearLayout 子高度都设置为 'wrap_content'。
这不会完全如你所愿,但至少它不会滚动,如果内容完全适合屏幕。
谈到您的通讯录应用示例,看起来它没有使用 CoordinatorLayout 和它附带的其他东西。
这种行为可以通过这种方式完成:
<ScrollView
android:id="@+id/scroll_adinfo"
android:layout_
android:layout_>
<FrameLayout
android:layout_
android:layout_
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_
android:layout_
android:src="@mipmap/ic_launcher"/>
<LinearLayout
android:id="@+id/layout_content"
android:layout_
android:layout_
android:paddingTop="@dimen/image_height">
<!-- YOUR CONTENT HERE -->
</LinearLayout>
</FrameLayout>
</ScrollView>
在您的代码中,您将在滚动时移动图像:
final ImageView image = (ImageView) findViewById(R.id.image);
((ScrollView) rootView.findViewById(R.id.scroll_adinfo)).getViewTreeObserver().addOnScrollChangedListener(
new ViewTreeObserver.OnScrollChangedListener()
@Override
public void onScrollChanged()
int scrollY = ((ScrollView) rootView.findViewById(R.id.scroll_adinfo)).getScrollY();
image.setY(scrollY / 2);
);
我已经从我的一个项目中提取了它并对其进行了编辑,因此我可以遗漏一些东西。
【讨论】:
“wrap_content”解决方案并不总是有效。例如,将第一项(在 LinearLayout 中)设置为示例中的卡片,将第二项设置为具有 textSize="20dp" 和 layout_marginTop="80dp" 的 TextView。它将允许滚动太多,直到操作栏折叠并且 TextView 下方有一个巨大的空白区域。在 Galaxy S4 上测试。但正如你所说,它并不完美。我不明白第二种解决方案。你试过了吗? @androiddeveloper 第二个解决方案代码是从我的一个项目中提取的。但它使用的是简单的 ScrollView。我不知道如何使它与 NestedScrollView 和 CoordinatorLayout 与 FloatingActionButton 一起工作。 @androiddeveloper 这个想法是将图像放在主要内容下方,并在用户滚动内容时以编程方式移动它。 好吧,这不是我问的... :( @androiddeveloper 这就是您的联系人应用示例中发生的情况。以上是关于如何禁用 NestedScrollView&CollapsingToolbarLayout 的滚动,例如当下面没有更多内容时?的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式在 NestedScrollView 中显示滚动条。
如何在Android中设置NestedScrollView的最大高度?
滚动 NestedScrollView 时如何获取 recyclerview 的当前项目位置?
当 Recyclerview 在 NestedScrollview 中时,如何避免绑定所有项目?