Programmatically make Scrollview not scrollable and change it back

464 Views Asked by At

I am trying to stop my scrollview scrolling when it reaches a scrollY position. Then, change it back to scrollable when children view (recyclerview) reach the top or overscrolled.

I tried two methods

  1. onTouchListener - it doesn't interact with scrollY position
nestedScrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return false;
}
});
  1. setOnScrollChangeListener - don't know how to set it as non-scrollable
nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY >400dp) {
     
}
}

Edit 1 XML

<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

     <Linearlayout
         android:layout_width="match_parent"
         android:layout_height="match_parent">
         //include more contents card
         <androidx.recyclerview.widget.RecyclerView

            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:nestedScrollingEnabled="true"
            android:orientation="vertical"
            />
          </LinearLayout>
</androidx.core.widget.NestedScrollView>
1

There are 1 best solutions below

2
einUsername On

Your idea with the OnTouchListener is working but the return value which Android expects is confusing. false means scrolling is activated. true means scrolling is deactivated.

This will prevent scrolling:

scrollView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return true;
    }
});

From View.java:

/**
* Called when a touch event is dispatched to a view. This allows listeners to
* get a chance to respond before the target view.
*
* @param v The view the touch event has been dispatched to.
* @param event The MotionEvent object containing full information about
*        the event.
* @return True if the listener has consumed the event, false otherwise.
*/
boolean onTouch(View v, MotionEvent event);

Edit:

You can use both of your Listeners and "connect" them through an instance variable. Like this:

    boolean freezeScrolling = false; //This has to be an instance variable so both Listeners can access it.

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);

        ScrollView scrollView = findViewById(R.id.scroll_view);
        Button button = findViewById(R.id.button);

        scrollView.setOnScrollChangeListener(new ScrollView.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
//                    Log.d("TAG", String.valueOf(scrollY));
                    freezeScrolling = (scrollY>100); //freeScrolling is set to true if scrollY is above 100. Otherwise to false.
            }
        });

        scrollView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return freezeScrolling; //onTouch returns the value which has been set in onScrollChange.
            }
        });

        //unfreezing test:
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                scrollView.scrollTo(0, 0); //freezeScrolling will be changed automatically.
            }
        });