22

I have a scrollView with lot of elements

ScrollView scroller = (ScrollView)findViewById(R.id.scrollView);

I need to attach an onClickListener to the scrollview so I do

scroller.setOnClickListener(new OnClickListener() { 
            @Override
            public void onClick(View v) { 
                // This is all you need to do to 3D flip
                AnimationFactory.flipTransition(viewAnimator, FlipDirection.LEFT_RIGHT); 
            }

        });

But this is not getting triggered when I touch. Any Ideas?

Oli
  • 3,496
  • 23
  • 32
Geo Paul
  • 1,777
  • 6
  • 25
  • 50
  • Are you sure it isn't getting triggered? Try to log it or show a Toast in the onClick method and see, if it works. – Ahmad May 27 '13 at 15:58
  • Yeah I tried that. Its not getting triggered. There are somany sub views inside that views is it because of that? – Geo Paul May 27 '13 at 15:59

6 Answers6

14

The best solution seem to put LinearLayout into ScrollView and set the setOnClickListener on it.

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/myLayout"
        android:clickable="true"
        android:orientation="vertical">

       <!-- content here -->
   </LinearLayout>
</ScrollView>

in the Activity :

LinearLayout lin = (LinearLayout) fragment.rootView.findViewById(R.id.myLayout);

lin.setOnTouchListener(new setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            // Whatever
        }
});
whlk
  • 15,487
  • 13
  • 66
  • 96
JulienGenoud
  • 592
  • 9
  • 21
  • Make sure you don't add padding to the ScrollView, otherwise those regions will not be clickable – Gibolt May 13 '20 at 20:41
13

You need to set the setOnClickListener directly on the ScrollView's child.

Since a ScrollView can have only one child, you can simply use this approach:

ScrollView scrollView = //...

View.OnClickListener mOnClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // ...
}

//Set the onClickListener directly on the ScrollView's child
scrollView.getChildAt(0).setOnClickListener(mOnClickListener);
bonnyz
  • 13,458
  • 5
  • 46
  • 70
  • 1
    The only problem with this solution is that you have to wait until the `ScrollView`'s child is added to the tree before you can setup the listener. It's not pretty, but I just used a post to wait for that to happen before I run `getChildAt(0)` – aaaidan Jan 18 '18 at 00:41
11

It is because the child of the ScrollView is getting the touch event of the user and not the ScrollView. You must set android:clickable="false" attribute to each and every child of the ScrollView for the onClickListener to work on ScrollView.

Or else the alternate could be to set the onClickListener on each of the ScrollView's children and handle it.

Atai Ambus
  • 89
  • 1
  • 2
  • 10
bakriOnFire
  • 2,685
  • 1
  • 15
  • 27
3

UPDATE 22/12/2020

sadly this also triggers after each scroll event.

This the actually the answer to the question without any odd cases by using View.OnTouchListener instead of View.OnClickListener on the ScrollView and detecting the MotionEvent.ACTION_UP where the finger is left off the screen.

To make sure that it's not a scroll, then save previous touched screen x, y values of the MotionEvent.ACTION_DOWN and compare it to those of MotionEvent.ACTION_UP. If they are not equal then certainly the user is moving their finger (i.e. scrolling) before they left it off the screen.

int mXOld, mYOld; // field values to save the tap down on the ScrollView 

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

        int x = (int) event.getX();
        int y = (int) event.getY();

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            mXOld = x;
            mYOld = y;
        } else if (event.getAction() == MotionEvent.ACTION_UP) {

            if (x == mXOld || y == mYOld) {  // detecting it's not a horizontal/vertical scrolling in ScrollView
                // HERE add the code you need when the ScrollView is clicked
                Toast.makeText(MainActivity.this, "Click", Toast.LENGTH_SHORT).show();
                return false;
            }
        }

        return false;
    }
});

Original Answer: Odd case that is different than the question

My problem was somehow different, so I wanted to share it..

I have a ScrollView that I have to use match_parent in its width & height; and I have an internal TextView that is centered in the ScrollView.

The text of the TextView can be long so it occupies the full height of the ScrollView, and sometimes it can be short, so there will be blank areas on the top and bottom., So setting the OnClickListener on the TextView didn't help me whenever the text is short as I want the blank areas detects the click event as well; and also the OnClickListener on the ScrollView doesn't work..

So, I solved this by setting OnTouchListener on the ScrollView and put code into MotionEvent.ACTION_UP So it can kind of simulating complete tap by lefting off the finger off the screen.

private View.OnTouchListener mScrollViewTouchListener = new View.OnTouchListener() {
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) { 
            // DO Something HERE...
        }
        return false;
    }
};
Zain
  • 37,492
  • 7
  • 60
  • 84
  • sadly this also triggers after each scroll event. – user2965003 Dec 22 '20 at 13:54
  • 1
    @user2965003 thanks for the comment.. you're totally right .. my shared case was something different than of the question .. as I just wanted to intercept the click on the `TextView` and that's working right without touching the `ScrollView`, but the problem is that when the `TextView` doesn't occupy the entire `ScrollView` height, in this case there is no Scroll in the `ScrollView`, and my issue is then the free/blank area at the `ScrollView` doesn't intercept the click as it's not a part of the `TextView`, So I used this .. let me check if i can solve your issue – Zain Dec 22 '20 at 16:36
  • 1
    @user2965003 I managed to solve your problem .. please check updated answer... hopefully works with you – Zain Dec 22 '20 at 18:36
  • 1
    Good job. I actually had to solve this yesterday and used a GestureDetector, overriding the onSingleTapUp. Had to post as standalone answer to show the code. – user2965003 Dec 23 '20 at 17:11
2

As @Zain pointed out, sometimes it is necessary to capture OnClicks for the whole area of the Scrollview, while the childs may be smaller or invisible. To circumvent scrolling detected as an onClick, we used a GestureDetector:

final protected GestureDetector gestureDetector = new GestureDetector(getActivity(),
            new GestureDetector.SimpleOnGestureListener() {

                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    return true;
                }

            });

in onCreateView

scrollView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(gestureDetector.onTouchEvent(event)){
                    [do stuff]
                }

                return false;
            }
        });
user2965003
  • 326
  • 2
  • 11
0

I think you can custom a ScrollView, override the dispatchTouchEvent method, add add the custom onClick callback.

Better
  • 165
  • 1
  • 13