44

I have a list view and depending on some logic I want to temporary disable the scrolling. view.setOnScrollListener(null); doesn't helps me I guess I need to write some code, can someone give me a hist or some snippet ?

Thanks

Lukap
  • 31,523
  • 64
  • 157
  • 244

10 Answers10

45

In your CustomListView:

@Override
public boolean dispatchTouchEvent(MotionEvent ev){
   if(ev.getAction()==MotionEvent.ACTION_MOVE)
      return true;
   return super.dispatchTouchEvent(ev);
}

Then ListView will react to clicks, but will not change scroll position.

Pointer Null
  • 39,597
  • 13
  • 90
  • 111
  • it was long ago when I had this problem , but I think this is the right solution (I solved my problem with managing the visibility ) – Lukap Jun 21 '12 at 15:25
  • 5
    Looks like this solution is just partial and will fail on any device with external keyboard or BT/USB keyboard attached. – sandrstar Sep 27 '12 at 10:01
  • 4
    Unfortunately this, also, simply doesn't work. You still get scrolling effects if you swipe over a button, or anything. – Fattie Jun 26 '14 at 17:28
  • use Nikita's Solution, that is simple way – Muhammed Haris Mar 24 '20 at 17:42
44

Another option without creating a new custom ListView would be to attach an onTouchListener to your ListView and return true in the onTouch() callback if the motion event action is ACTION_MOVE.

listView.setOnTouchListener(new OnTouchListener() {

    public boolean onTouch(View v, MotionEvent event) {
        return (event.getAction() == MotionEvent.ACTION_MOVE);
    }
});
Surya Wijaya Madjid
  • 1,823
  • 1
  • 19
  • 25
  • 1
    Or, which is shorter and cleaner, `return (event.getAction() == MotionEvent.ACTION_MOVE);` – aga May 30 '14 at 13:33
  • 8
    this **DOES NOT WORK AT ALL** if the ListView contains ANY buttons or indeed even a pull to refresh. It simply **does not work**, if the users happens to scroll over one of the buttons - it scrolls. It's not usable at all. – Fattie Jun 26 '14 at 16:00
33

use listview.setEnabled(false) to disable listview scrolling

Note: This also disables row selections

loadedion
  • 2,217
  • 19
  • 41
Nikita
  • 347
  • 3
  • 4
9

make your CustomListView

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
  if(needToStop){
    return false;}
    return super.onInterceptTouchEvent(ev); 
}

on false the childs will handle the touch event, make sure you put your if condition to check you need to scroll or not.

Mohammad Ersan
  • 12,304
  • 8
  • 54
  • 77
  • Well this is very good hint by doesn't work for me. Here is the problem I need to disable scroll while I am in onTouch, and return false in onTouch doesn't disable scroll. :( – Lukap Oct 03 '11 at 08:59
  • :), you didn't understand me. I can't return false in onInterceptTouchEvent, cause I need all others method to be called, methods like onTouch btw(I am makeing drag and drop app ). That is why ones I pressed the onInterceptTouchEvent i called but I do not release my finger and the onTouch is called many times so here I want to disable scrolling.... – Lukap Oct 03 '11 at 10:22
  • 1
    @MoshErsan : Can you explain how we can disable parent TochEvent while the child need it for inner scrolling? I really need this. – Soheil Setayeshi Jul 18 '13 at 09:49
8

If you have an event bound to the list items, then dragging the list with any of these solutions will still trigger the event. You want to use the following method to account for users expectation to cancel the event by dragging away from the selected item (adapted from Pointer Null's answer):

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK;

    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Record the position the list the touch landed on
        mPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
        return super.dispatchTouchEvent(ev);
    }

    if (actionMasked == MotionEvent.ACTION_MOVE) {
        // Ignore move events
        return true;
    }

    if (actionMasked == MotionEvent.ACTION_UP) {
        // Check if we are still within the same view
        if (pointToPosition((int) ev.getX(), (int) ev.getY()) == mPosition) {
            super.dispatchTouchEvent(ev);
        } else {
            // Clear pressed state, cancel the action
            setPressed(false);
            invalidate();
            return true;
        }
    }

    return super.dispatchTouchEvent(ev);
}

Full custom view class is available: https://gist.github.com/danosipov/6498490

Dan Osipov
  • 1,429
  • 12
  • 15
5

The best answer for me is the One from Dan Osipov. I'd improve it like this. (firing a CANCEL action event instead of manually erasing the pressed state).

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK;

    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Record the position the list the touch landed on
        mPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
        return super.dispatchTouchEvent(ev);
    }

    if (actionMasked == MotionEvent.ACTION_MOVE) {
        // Ignore move events
        return true;
    }

    if (actionMasked == MotionEvent.ACTION_UP) {
        // Check if we are still within the same view
        if (pointToPosition((int) ev.getX(), (int) ev.getY()) != mPosition) {
            // Clear pressed state, cancel the action
            ev.setAction(MotionEvent.ACTION_CANCEL);
        }
    }

    return super.dispatchTouchEvent(ev);
}
Mauro Panzeri
  • 106
  • 2
  • 6
2

When writing code for swipe to delete on a list view, I wanted to prevent the vertical scrolling once the swipe had been detected. I set a boolean flag in the ACTION_MOVE section once the swipe to delete conditions had been met. The dispatchTouchEvent method checks that condition and prevents the scroll from working. In the ACTION_UP I set the flag back to false to re-enable the scroll.

private float mY = Float.NaN;
private boolean mStopScroll = false;

@Override
public boolean onTouch(View view, MotionEvent event) {

   if(!mStopScroll) {
       mY = event.getY();
   }

   switch (event.getAction()) {

        case MotionEvent.ACTION_MOVE:

            if(<condition that stops the vertical scroll>) {
                mStopScroll = true;
            }

            break;

        case MotionEvent.ACTION_UP:

            mStopScroll = false;

            // your code here
            return true;

        default:

    }

    return false;

}

@Override
public boolean dispatchTouchEvent(MotionEvent motionEvent) {

    if(mStopScroll) {
        motionEvent.setLocation(motionEvent.getX(), mY);
    }
    return super.dispatchTouchEvent(motionEvent);
}
vilvic
  • 113
  • 8
1

My answer will be interesting for Xamarin and MvvmCross users. Main concept is the same like in previous posts, so main steps are:

  1. Mute scroll events
  2. Change dynamically list height

Here you are helper class, that allows to disable scroll in list view:

using System;
using Cirrious.MvvmCross.Binding.Droid.Views;
using Android.Content;
using Android.Util;
using Android.Views;
using Android.Database;

namespace MyProject.Android.Helpers
{
    public class UnscrollableMvxListView
        : MvxListView
    {
        private MyObserver _myObserver;

        public UnscrollableMvxListView (Context context, IAttributeSet attrs, MvxAdapter adapter)
            : base(context, attrs, adapter)
        {
        }

        protected override void OnAttachedToWindow ()
        {
            base.OnAttachedToWindow ();
            var dtso = new MyObserver(this);
            _myObserver = dtso;
            Adapter.RegisterDataSetObserver (dtso);
        }

        protected override void OnDetachedFromWindow ()
        {
            Log.Debug ("UnscrollableMvxListView", "OnDetachedFromWindow");
            if (_myObserver != null) {
                Adapter.UnregisterDataSetObserver (_myObserver);
                _myObserver = null;
            }
            base.OnDetachedFromWindow ();
        }

        //Make List Unscrollable
        private int _position;
        public override bool DispatchTouchEvent (MotionEvent ev)
        {
            MotionEventActions actionMasked = ev.ActionMasked & MotionEventActions.Mask;

            if (actionMasked == MotionEventActions.Down) {
                // Record the position the list the touch landed on
                _position = PointToPosition((int) ev.GetX (), (int) ev.GetY());
                return base.DispatchTouchEvent(ev);
            }

            if (actionMasked == MotionEventActions.Move) {
                // Ignore move events
                return true;
            }

            if (actionMasked == MotionEventActions.Up) {
                // Check if we are still within the same view
                if (PointToPosition((int) ev.GetX(), (int) ev.GetY()) == _position) {
                    base.DispatchTouchEvent(ev);
                } else {
                    // Clear pressed state, cancel the action
                    Pressed = false;
                    Invalidate();
                    return true;
                }
            }

            return base.DispatchTouchEvent(ev);
        }

        //Make List Flat
        public void JustifyListViewHeightBasedOnChildren () {
            if (Adapter == null) {
                return;
            }
            var vg = this as ViewGroup;
            int totalHeight = 0;
            for (int i = 0; i < Adapter.Count; i++) {
                View listItem = Adapter.GetView(i, null, vg);
                listItem.Measure(0, 0);
                totalHeight += listItem.MeasuredHeight;
            }

            ViewGroup.LayoutParams par = LayoutParameters;
            par.Height = totalHeight + (DividerHeight * (Adapter.Count - 1));
            LayoutParameters = par;
            RequestLayout();
        }
    }

    internal class MyObserver
        : DataSetObserver 
    {
        private readonly UnscrollableMvxListView _unscrollableMvxListView;

        public MyObserver (UnscrollableMvxListView lst)
        {
            _unscrollableMvxListView = lst;
        }

        public override void OnChanged() {
            Log.Debug("UnscrollableMvxListView", "OnChanged");
            base.OnChanged ();
            _unscrollableMvxListView.JustifyListViewHeightBasedOnChildren ();
        }
    }
}
user1700099
  • 466
  • 5
  • 6
1

This is the code Joe Blow (comment on OP post) pointed at on http://danosipov.com/?p=604 but I'm posting it here to preserve it in case the link is orphaned:

public class ScrollDisabledListView extends ListView {

private int mPosition;

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

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

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

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK;

    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Record the position the list the touch landed on
        mPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
        return super.dispatchTouchEvent(ev);
    }

    if (actionMasked == MotionEvent.ACTION_MOVE) {
        // Ignore move events
        return true;
    }

    if (actionMasked == MotionEvent.ACTION_UP) {
        // Check if we are still within the same view
        if (pointToPosition((int) ev.getX(), (int) ev.getY()) == mPosition) {
            super.dispatchTouchEvent(ev);
        } else {
            // Clear pressed state, cancel the action
            setPressed(false);
            invalidate();
            return true;
        }
    }

    return super.dispatchTouchEvent(ev);
}
}

When you add this ListView to your layout remember to precede it with its package name, otherwise an exception will be thrown when you try to inflate it.

Aspiring Dev
  • 505
  • 1
  • 4
  • 17
0

Try this:

listViewObject.setTranscriptMode(0);

Works for me.

josliber
  • 43,891
  • 12
  • 98
  • 133
gopalj
  • 9
  • 2