5

I have a NestedScrollView being used with CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout with parallax effect similar to this tutorial

I need to scroll the content programmatically (preferably a smooth scroll, i.e. animated), however calling the scroll methods (scrollBy(), scrollTo(), smoothScrollTo(), smoothScrollBy()) do nothing.

Note that I am using app:layout_behavior="@string/appbar_scrolling_view_behavior" <-- Not sure if the issue is related to this.

I'm calling nsv_form.smoothScrollBy(0, 300) in Kotlin when a button is clicked by the user, but nothing happens :(

(Also tried scrollTo(), scrollBy(), +- 300, all sorts of different variations)

UPDATE: I dug into source code and it seems like the *scroll*() methods expect the content of the layout to be larger than the parent view (makes sense). In my case, the content is smaller, so I suspect that's why the scrolling methods do not work. Perhaps I need something different instead of scroll?

The NestedScrollView's position starts partially off the screen with an image above it in a CollapsingToolbarLayout, like this, so it seems like I need to programmatically move the position of the NestedScrollView AND trigger the CoordinatorLayout's scrolling behavior. -- How do I do this?

Here's my layout:

<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/transparent"> <com.google.android.material.appbar.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:expandedTitleMarginEnd="64dp" app:expandedTitleMarginStart="48dp" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <ImageView android:id="@+id/iv_image" android:layout_width="match_parent" android:layout_height="@dimen/image_height" android:scaleType="centerCrop" app:layout_collapseMode="parallax" tools:src="@drawable/some_image" /> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.core.widget.NestedScrollView android:id="@+id/nsv_form" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:animateLayoutChanges="true" android:orientation="vertical"> [... child views...] </LinearLayout> </androidx.core.widget.NestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout> 

TLDR: How do I scroll like this programmatically?

2
  • I think you should go for scrollview if you have only one child view as there is only one child layout in your case , which is LinearLayout. Commented Dec 20, 2018 at 23:47
  • @SumitShukla I tried switching NestedScrollView with ScrollView and it breaks the parallax scrolling behavior (the layout does not scroll at all) Commented Dec 21, 2018 at 0:00

2 Answers 2

4
+50

How do I scroll like this programmatically?

For that scrolling behavior you need collapse or expand CollapsingToolbarLayout no need to scroll your the NestedScrollView

Here is the sample code for that

Try this Make some below changes in your Layout

<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/rootView" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/transparent" android:fitsSystemWindows="true"> <com.google.android.material.appbar.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:expandedTitleMarginEnd="64dp" app:expandedTitleMarginStart="48dp" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <ImageView android:id="@+id/iv_image" android:layout_width="match_parent" android:layout_height="250dp" android:adjustViewBounds="true" android:scaleType="fitXY" android:src="@drawable/goku" app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="0.7" /> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" app:contentInsetLeft="0dp" app:contentInsetStart="0dp" app:contentInsetStartWithNavigation="0dp" app:titleTextAppearance="@style/AppTheme.Toolbar.Title" app:popupTheme="@style/AppTheme.PopupOverlay" /> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:background="@color/Boxcolordiabled" app:layout_anchor="@id/app_bar" app:layout_anchorGravity="bottom|end" app:srcCompat="@drawable/ic_favorite" /> <androidx.core.widget.NestedScrollView android:id="@+id/nsv_form" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_light" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:animateLayoutChanges="true" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="@string/demo" /> <Button android:id="@+id/btnColl" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="Expand " /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="@string/demo" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="@string/demo" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="@string/demo" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="@string/demo" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="@string/demo" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="@string/demo" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="@string/demo" /> </LinearLayout> </androidx.core.widget.NestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout> 

Activity code

import android.animation.ValueAnimator; import android.os.Bundle; import android.view.View; import android.view.animation.DecelerateInterpolator; import android.widget.Button; import android.widget.Toast; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.floatingactionbutton.FloatingActionButton; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.coordinatorlayout.widget.CoordinatorLayout; import neel.com.bottomappbar.R; public class MainActivity extends AppCompatActivity { Toolbar toolbar; AppBarLayout app_bar; Button btnColl; FloatingActionButton fab; CoordinatorLayout rootView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("Stack Demo"); app_bar = findViewById(R.id.app_bar); btnColl = findViewById(R.id.btnColl); fab = findViewById(R.id.fab); rootView = findViewById(R.id.rootView); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, "Collapse FAB Clicked", Toast.LENGTH_SHORT).show(); CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) app_bar.getLayoutParams(); final AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior(); if (behavior != null) { ValueAnimator valueAnimator = ValueAnimator.ofInt(); valueAnimator.setInterpolator(new DecelerateInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { behavior.setTopAndBottomOffset((Integer) animation.getAnimatedValue()); app_bar.requestLayout(); } }); valueAnimator.setIntValues(0, -900); valueAnimator.setDuration(1000); valueAnimator.start(); } } }); btnColl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, "Expand btnColl Clicked", Toast.LENGTH_SHORT).show(); CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) app_bar.getLayoutParams(); final AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior(); if (behavior != null) { ValueAnimator valueAnimator = ValueAnimator.ofInt(); valueAnimator.setInterpolator(new DecelerateInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { behavior.setTopAndBottomOffset((Integer) animation.getAnimatedValue()); app_bar.requestLayout(); } }); valueAnimator.setIntValues(-900, 0); valueAnimator.setDuration(400); valueAnimator.start(); } } }); } } 

OUTPUT

https://www.youtube.com/watch?v=nZY1zPxjRt0

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks!! This is almost perfect!! However, I've run into one problem -- the content inside my LinearLayout exceeds the screen height, and this solution stops once the AppBarLayout is fully collapsed. I've tried scrolling the NestedScrollView once AppBarLayout is fully collapsed, however it's not smooth. if (appBarLayout.bottom == 0) { nsv_form.scrollTo(0, -(animation.animatedValue as Int)) // Also tried smoothScrollTo() } <-- Abrupt animation
@tenprint check this for the smooth scroll in your NestedScrollView stackoverflow.com/a/46171939/7666442
1

Scrolls (scrollBy()/scrollTo()/smoothScrollTo()/smoothScrollBy()) needs to be called from UI thread.

In Kotlin, you can use

Handler().post { nsv_form.scrollBy(0, 300) } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.