Disable ScrollView Programmatically?
Asked Answered
R

19

117

I would like to enable ScrollView and disable it by a Button Click.
Disable means like if the ScrollView wasn't there, and enable it returns the ScrollView.

I want that because I have a gallery with text images, and on a button click the screen orientation changes, so in Landscape the text becomes bigger. And I want the ScrollView so the image does not stretch itself and the text becomes unreadable.

scrollview.Enabled=false / setVisibility(false) doesn't make anything.

xml:

<ScrollView 
android:id="@+id/QuranGalleryScrollView" 
android:layout_height="fill_parent" 
android:layout_width="fill_parent">
<Gallery android:id="@+id/Gallery" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent"
android:scrollbars="horizontal"></Gallery>
</ScrollView>

I can't use Visibility (gone) since that would also hide the Gallery, what I want is to hide the effect of the ScrollView. When there is ScrollView, the images in Gallery become scrollable and do not fit in the screen so you have to scroll to see the whole image. I don't want to disable/enable that on a button click.

I tried this:

((ScrollView)findViewById(R.id.QuranGalleryScrollView)).setOnTouchListener(null);
                        ((ScrollView)findViewById(R.id.QuranGalleryScrollView)).setHorizontalScrollBarEnabled(false);
                        ((ScrollView)findViewById(R.id.QuranGalleryScrollView)).setVerticalScrollBarEnabled(false);
                        ((ScrollView)findViewById(R.id.QuranGalleryScrollView)).setEnabled(false);

But still the images in the Gallery are scrollable and not fit the screen. What's the solution to this?

Redheaded answered 23/4, 2011 at 9:3 Comment(0)
T
201

Several points to begin with:

  1. You cannot disable the scrolling of a ScrollView. You would need to extend to ScrollView and override the onTouchEvent method to return false when some condition is matched.
  2. The Gallery component scrolls horizontally regardless of whether it is in a ScrollView or not - a ScrollView provides only vertical scrolling (you need a HorizontalScrollView for horizontal scrolling)
  3. You seem to say you have a problem with the image stretching itself -- this has nothing to do with the ScrollView, you can change how an ImageView scales with the android:scaleType property (XML) or the setScaleType method - for instance ScaleType.CENTER will not stretch your image and will center it at it's original size

You could modify ScrollView as follows to disable scrolling

class LockableScrollView extends ScrollView {

    ...

    // true if we can scroll (not locked)
    // false if we cannot scroll (locked)
    private boolean mScrollable = true;

    public void setScrollingEnabled(boolean enabled) {
        mScrollable = enabled;
    }

    public boolean isScrollable() {
        return mScrollable;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // if we can scroll pass the event to the superclass
                return mScrollable && super.onTouchEvent(ev);
            default:
                return super.onTouchEvent(ev);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // Don't do anything with intercepted touch events if
        // we are not scrollable
        return mScrollable && super.onInterceptTouchEvent(ev);
    }

}

You would then use

<com.mypackagename.LockableScrollView 
    android:id="@+id/QuranGalleryScrollView" 
    android:layout_height="fill_parent" 
    android:layout_width="fill_parent">

    <Gallery android:id="@+id/Gallery" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:scrollbars="horizontal">
    </Gallery>

</com.mypackagename.LockableScrollView>

in your XML file (just changed the ScrollView to your special LockableScrollView).

Then call

((LockableScrollView)findViewById(R.id.QuranGalleryScrollView)).setScrollingEnabled(false);

to disable scrolling of the view.

I think that you have more than just the issue of disabling scrolling though to achieve your desired result (the gallery will remain scrollable with the above code for instance) - I'd recommend doing some more research on each of the three components (Gallery, ScrollView, ImageView) to see what properties each one has and how it behaves.

Tarmac answered 23/4, 2011 at 11:9 Comment(10)
Here is how the class looks: pastebin.com/dsWSWR4x But I get the error 'The method onTouch(View, MotionEvent) of type LockableScrollView must override or implement a supertype method'Redheaded
Apologies, corrected the errors in my code - it should have been onTouchEvent(MotionEvent ev)Tarmac
Ok now the code complies, though when I run the app it crashes... and when i remove this class and return the <ScrollView instead of your class the app works fine!Redheaded
Hi Omar could you post the error you get? Make sure change the com.mypackagename in <com.mypackagename.LockableScrollView> to your own oneTarmac
At the end I was able to get it to work, though I run into other issues so I used a dirty fix by having 2 Galleries, one within the scrollview and the other out, and i will just hide/unhide them.. thanks for your time thoughRedheaded
Merged Beowulf Bjornson's answer into this.Tarmac
This code works great, but for some reason the scrollview is reset to the top on every touch event so you get this really jumpy scroll. Has anyone else had to deal with that?Escadrille
I want to compute the value of mScrollable by calculating the horizontal deflection of my touch movement inside onInterceptTouchEvent method. Can anyone tell me for a ScrollView how do I compute this inside onInterceptTouchEventStepson
Good answer. If you change case MotionEvent.ACTION_DOWN: to case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_DOWN: you will be also able to disable scroll also while dragging. But it is of course on everyone how he needs to use it.Territory
Just as luky mentioned, an additional MotionEvent.ACTION_MOVE is required to make this complete. Developer must be able to disable/enable scrolling while gesture is underway.Yb
C
79

I had gone this way:

        scrollView.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            return isBlockedScrollView;
        }
    });
Corner answered 15/3, 2013 at 7:22 Comment(6)
Simple and works great. The key point is to use a variable (e.g., isBlockedScrollView) to control when you want to enable scrolling. Set it to true when scrolling should be allowed and false otherwise.Ineffable
@PeteH, I have no idea why, but my ListView works the opposite way: true when scrolling is disabled and false when it is enabled.Tomlinson
@Holmes: you are right, true when you want to block scrollingCorner
Works beautifully.Also, I have children views (buttons) within the scrollview and I can still interact with them despite not overriding onInterceptTouchEvent()! Elegant solution.Fled
@Tomlinson The reason you must return true in order to block scrolling is because the touch listener takes the boolean value returned in order to determine whether or not the touch event should consumed or not. If you return false the scroll view will then proceed to pass the touch event to its scroll listener.Fehr
nice. I liked it.Ecstatic
S
15

Found this simple solution just set

ScrollView.requestDisallowInterceptTouchEvent(true);
Suboxide answered 20/1, 2014 at 10:48 Comment(2)
requestDisallowInterceptTouchEvent only works for the child of the View to be locked. So it would be more like mLinearLayout.requestDisallowInterceptTouchEvent(true);Glyco
it's just what i need. thx.Allfired
Y
9

Disablend ScrollView

ScrollView sw = (ScrollView) findViewById(R.id.scrollView1);
sw.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return true;
    }
});
Yuk answered 11/9, 2013 at 5:6 Comment(0)
T
5

I don't have enough points to comment on an answer, but I wanted to say that mikec's answer worked for me except that I had to change it to return !isScrollable like so:

mScroller.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
      return !isScrollable;
    }
});
Titoism answered 21/9, 2012 at 13:17 Comment(0)
C
5

to start, i used the Code posted posted in the first Comment but i changed it like this:

    public class LockableScrollView extends ScrollView {

    public LockableScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }
    public LockableScrollView(Context context, AttributeSet attrs) 
    {
        super(context, attrs);
    }

    public LockableScrollView(Context context) 
    {
        super(context);
    }

    // true if we can scroll (not locked)
    // false if we cannot scroll (locked)
    private boolean mScrollable = true;

    public void setScrollingEnabled(boolean enabled) {
        mScrollable = enabled;
    }

    public boolean isScrollable() {
        return mScrollable;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // if we can scroll pass the event to the superclass
                if (mScrollable) return super.onTouchEvent(ev);
                // only continue to handle the touch event if scrolling enabled
                return mScrollable; // mScrollable is always false at this point
            default:
                return super.onTouchEvent(ev);
        }
    }



@Override  
public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {     
    case MotionEvent.ACTION_DOWN:         
        // if we can scroll pass the event to the superclass      
        if (mScrollable) return super.onInterceptTouchEvent(ev);      
        // only continue to handle the touch event if scrolling enabled    
        return mScrollable; // mScrollable is always false at this point     
        default:          
            return super.onInterceptTouchEvent(ev);      
            }
    }

}

then i called it in by this way

((LockableScrollView)findViewById(R.id.scrollV)).setScrollingEnabled(false);

because i tried

((LockableScrollView)findViewById(R.id.scrollV)).setIsScrollable(false);

but it said that setIsScrollable is undefined

i hope this will help you

Crockery answered 27/2, 2014 at 11:17 Comment(2)
Note: the first two constructors are needed if your ScrollView is the main layout container of your layout class.Modifier
If you touch a button or something else that intercept the touches first, the scrollview will still scrollHanson
W
3

Here is a simpler solution. Override the onTouch() for the ScrollView OnTouchListener and return false if you want to bypass the scroll by touch. The programmatic scrolling still works and no need to extend the ScrollView class.

mScroller.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
  return isScrollable;
}
});
Wadai answered 26/6, 2012 at 15:43 Comment(1)
!isScrollable since true return value means that the event was intercepted.Crosscut
A
3

I had a layout over the NestedScrollView consuming the touch event and disable the scroll:

progressBarLayout.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                return true;
            }
        });

and to enable:

progressBarLayout.setOnTouchListener(null);
Auxin answered 19/12, 2016 at 12:54 Comment(1)
I used the setOnTouchListener on a androidx.core.widget.NestedScrollView and this worked great. Then set it to null to enable it back.Incidence
F
2

Extend ScrollView and override onInterceptTouchEvent to return false, in Kotlin:

class SV(context: Context) : HorizontalScrollView(context) {
   var allowUserScroll = false

   override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
      return if (allowUserScroll) super.onInterceptTouchEvent(ev) else false
   }
}

That's enough!

(The advantage over using setOnTouchListener is that this way the disabled ScrollView does not interfere with touch event handling of descendants in any way)

Forgat answered 29/3, 2022 at 15:13 Comment(0)
N
1
@Override  
public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {     
    case MotionEvent.ACTION_DOWN:         
        // if we can scroll pass the event to the superclass      
        if (mScrollable) return super.onInterceptTouchEvent(ev);      
        // only continue to handle the touch event if scrolling enabled    
        return mScrollable; // mScrollable is always false at this point     
        default:          
            return super.onInterceptTouchEvent(ev);      
            }
    }
Noble answered 13/7, 2011 at 21:54 Comment(0)
S
1

@JosephEarl +1 He has a great solution here that worked perfectly for me with some minor modifications for doing it programmatically.


Here are the minor changes I made:

LockableScrollView Class:

public boolean setScrollingEnabled(boolean enabled) {
    mScrollable = enabled;
    return mScrollable;
}

MainActivity:

LockableScrollView sv;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    sv = new LockableScrollView(this);
    sv.setScrollingEnabled(false);
}
Sprag answered 7/11, 2012 at 21:4 Comment(0)
D
1

You can make a CustomScrollView, for which you can disable its interference with other views. Though this can be sometimes irritating for the end user. Use it with caution.

This is the code:

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewParent;

public class CustomScrollView extends android.widget.ScrollView {

    public CustomScrollView(Context context) {
        super(context);
    }

    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        /* Prevent parent controls from stealing our events once we've gotten a touch down */
        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN)
        {
            ViewParent p = getParent();
            if (p != null)
                p.requestDisallowInterceptTouchEvent(true);
        }

        return false;
    }
}

How to use the CustomScrollView?

You can add CustomScrollView as a parent to the screen in which you want to add another Scrollview as a child view, and the whole screen is scrollable. I used this for a RelativeLayout.

<?xml version="1.0" encoding="utf-8"?>
<**package.**CustomScrollView 
xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/some_id"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    xmlns:tools="http://schemas.android.com/tools">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    #**Inside this I had a TableLayout and TableRow. Inside the TableRow,**
    #**I had a TextView which I made scrollable from Java Code.**
    #**textView.setMovementMethod(new ScrollingMovementMethod());**
    </RelativeLayout>
</ankit.inventory.ankitarora.inventorymanagementsimple.CustomScrollView>

It worked for my case.

Decide answered 2/3, 2017 at 18:58 Comment(0)
S
1
public class LockableScrollView extends ScrollView {

    private boolean mScrollable = true;

    public LockableScrollView(Context context) {
        super(context);
    }

    public LockableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public LockableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public LockableScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public void setScrollable(boolean enabled) {
        mScrollable = enabled;
    }

    public boolean isScrollable() {
        return mScrollable;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return mScrollable && super.onTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mScrollable && super.onInterceptTouchEvent(ev);
    }
}
Samothrace answered 15/3, 2018 at 15:32 Comment(0)
A
1

Kotlin:

scrollView.setOnTouchListener { _, _ -> true }
Asta answered 14/7, 2021 at 5:27 Comment(0)
T
0

Does this help?

((ScrollView)findViewById(R.id.QuranGalleryScrollView)).setOnTouchListener(null);
Tavis answered 23/4, 2011 at 9:9 Comment(1)
I dont think it did any difference, I still have the same issueRedheaded
W
0

You can extend the gallery and use some flag to disable scrolling when you want:

public class MyGallery extends Gallery {

public boolean canScroll;

public MyGallery(Context context, AttributeSet attrs) {
    canScroll = true;
    super(context, attrs);
}

public void setCanScroll(boolean flag)
{
    canScroll = flag;
}

@Override
public boolean onScroll(android.view.MotionEvent e1, android.view.MotionEvent e2, float distanceX, float distanceY) {
    if (canScroll)
        return super.onScroll(e1,e2,distancex,distancey);
    else
        return false;
}

@Override
public boolean onSingleTapUp(MotionEvent e)
{
    if (canScroll)
        return super.onSingleTapUp(ey);
    else
        return false;
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
    if (canScroll)
        return super.onFling(e1,e2,velocityX,velocityY);
    else
        return false;
}
}
Wunder answered 28/10, 2012 at 0:36 Comment(0)
J
0

Here is a Kotlin extension

   @SuppressLint("ClickableViewAccessibility")
fun NestedScrollView.disableScroll() {
    this.setOnTouchListener { _, _ ->
        true
    }
}

@SuppressLint("ClickableViewAccessibility")
fun NestedScrollView.enableScroll() {
    this.setOnTouchListener { _, _ ->
        false
    }
}

@SuppressLint("ClickableViewAccessibility")
fun ScrollView.disableScroll() {
    this.setOnTouchListener { _, _ ->
        true
    }
}

@SuppressLint("ClickableViewAccessibility")
fun ScrollView.enableScroll() {
    this.setOnTouchListener { _, _ ->
        false
    }
}
Janiecejanifer answered 22/4, 2022 at 10:13 Comment(0)
W
-6

on button click listener just do

ScrollView sView = (ScrollView)findViewById(R.id.ScrollView01);

sView.setVerticalScrollBarEnabled(false);
sView.setHorizontalScrollBarEnabled(false);

so that scroll bar is not enabled on button click

Wryneck answered 23/4, 2011 at 9:9 Comment(2)
This only hides the bars I guess.. but not the effect of the scrollviewRedheaded
it won't allow you to scroll also it will not hide gallery viewWryneck
E
-7

As you can see in the documentation, you cannot set the visibility to false. In your case you should probably use:

scrollview.setVisibility(Visibility.GONE);
Ephebe answered 23/4, 2011 at 9:8 Comment(2)
This will also hide the Gallery so its not the solution, I edited the post with more detailsRedheaded
OMG How you can post this as serious answer...Liege

© 2022 - 2024 — McMap. All rights reserved.