60

I set a click listener on a ViewPager, but the onClick event is never called. I guess the touch event detection of the ViewPager is interfering, but I can't see how to solve it...

Anybody could help?

Thanks

mViewPager.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        // never called
    }
}); 
jul
  • 36,404
  • 64
  • 191
  • 318
  • 1
    what you want to do with Click that will may help to give another solution. – Herry Apr 20 '12 at 09:47
  • I want something like ListView's onItemClickListener() – jul Apr 20 '12 at 09:57
  • mean you want to need known which item is click in ViewPager .that means which Page Position is click ,Am i right?. – Herry Apr 20 '12 at 10:01
  • Actual listview do not conflict with viewpagers. Setting onClickListeners under the ViewPager should work fine too. – bhekman Jul 03 '12 at 14:17

8 Answers8

106

I solved a similar problem by using a GestureDetector

Sending the MotionEvent to the GestureDetector

tapGestureDetector = new GestureDetector(this, new TapGestureListener());

viewPager.setOnTouchListener(new OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            tapGestureDetector.onTouchEvent(event);
            return false;
        }
});

It you are using the compatibility library, you can change the first line to:

tapGestureDetector = new GestureDetectorCompat(this, new TapGestureListener());

You can handle your Event in the GestureListener:

        class TapGestureListener extends GestureDetector.SimpleOnGestureListener{

         @Override
         public boolean onSingleTapConfirmed(MotionEvent e) {
           // Your Code here
         }
        }
justisb
  • 7,081
  • 2
  • 26
  • 44
Martin Christmann
  • 2,488
  • 2
  • 21
  • 17
  • 1
    Thanks, this worked for me since I couldn't extend ViewPager. – n0rm1e Jun 29 '13 at 08:38
  • 1
    This is what is looking for +1 for this – Manwal Apr 11 '14 at 14:59
  • 5
    Consider [`onSingleTapUp`](https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html#onSingleTapUp%28android.view.MotionEvent%29) instead of `onSingleTapConfirmed` if you want the event to be triggered when you click, like `setOnClickListener` would do. Otherwise the event will be triggered after some time, making sure it's not followed by a second tap. – minipif Jul 19 '14 at 15:07
  • 2
    Thanks a lot! Working perfectly! – Hoàng Toản Apr 16 '15 at 15:04
23

I done like that...

mViewPager.setOnTouchListener(new View.OnTouchListener() {
    float oldX = 0, newX = 0, sens = 5;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            oldX = event.getX();
            break;

        case MotionEvent.ACTION_UP:
            newX = event.getX();
            if (Math.abs(oldX - newX) < sens) {
                itemClicked(mViewPager.getCurrentItem());
                return true;
            }
            oldX = 0;
            newX = 0;
            break;
        }

        return false;
    }
});
nikib3ro
  • 20,366
  • 24
  • 120
  • 181
Kalpesh
  • 1,767
  • 19
  • 29
21

Indeed the viewpager is interfering. But you can override the methods of the viewpager to make it do what you want. You'll need to override the ViewGroup.onInterceptTouchEvent(MotionEvent ev) method.

You could just always return false to allow the touch events to fall through. I would also recommend calling super.onInterceptTouchEvent(ev) to allow swipes to keep working.

It passes in a MotionEvent, so you could check for clicks with that if you wanted.

Hope that helps. It should at least get you started. Post back with questions or further problems.

EDIT:

Note also that the ViewPager doesn't consume clicks. Therefore you could easily set an onclicklistener on any or all of the children of the viewpager if you wanted to capture clicks and potentially save yourself a lot of work.

bhekman
  • 3,227
  • 21
  • 24
  • does this go within my PagerAdapter subclass? How/where would this override be written? – CQM Jan 28 '13 at 01:54
  • 3
    @CQM also you can implement it as ViewPager.setOnTouchListener() – Viacheslav Sep 04 '13 at 09:28
  • For me, in ViewPager's OnTouchListener, it does not forward the ACTION_DOWN event, just starts with the ACTION_MOVE, followed by ACTION_UP when you drag, and it doesn't send anything when you just click it, so seems like overriding the onInterceptTouchEvent may be the only solution. – reactive-core Oct 23 '15 at 21:50
5

I know it's kinda old topic but I think this is much easier solution.

ViewPager viewPager = new ViewPager(this);
viewPager.setAdapter(yourPagerAdapter);

// somewhere where you setup your viewPager add this
viewPager.setOnTouchListener(
    new View.OnTouchListener() {
        private boolean moved;

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                moved = false;
            }
            if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
                moved = true;
            }
            if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                if (!moved) {
                    view.performClick();
                }
            }

            return false;
        }
    }
);

// then you can simply use the standard onClickListener ...
viewPager.setOnClickListener(
    new View.OnClickListener() {

        @Override
        public void onClick(View view) {
            Log.i(LOG, "Dayum!");
        }
    }
);
MarekZeman91
  • 109
  • 1
  • 3
3

While this is not an direct answer to how get the onClick triggered, it might very well be an useful answer to the related problem - capturing click events in ViewPager.

Simply add the onClick attribute in your layout xml file for the pager item and add the method in your activity.

Sample code:

PagerAdapter:

class SamplePagerAdapter extends PagerAdapter {
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // Inflate a new layout from our resources
        View view = getActivity().getLayoutInflater().inflate(R.layout.pager_item,
                container, false);
        // Add the newly created View to the ViewPager
        container.addView(view);
        // ... 

layout pager_item.xml

<?xml version="1.0" encoding="utf-8"?>

  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    // usual attributes
  >

    <ImageButton 
      // usual attributes
      android:onClick="handleAction"
    />

 </LinearLayout>

MainActivity:

  public class MainActivity extends Activity {
  /* 
   ...
  */
    public void handleAction(View view) {
      // do your stuff
    }
  }
J.G.Sebring
  • 5,934
  • 1
  • 31
  • 42
  • _Could not find method handleAction(View) in a parent or ancestor Context for android:onClick attribute defined on view class_ Getting this error @Tapirboy – tahsinRupam Jul 21 '19 at 07:29
  • @tahsinRupam method handleAction(View) is your own custom method, you need to implement it. – J.G.Sebring Aug 15 '19 at 13:54
0

You can set the focusability of the ViewPager so that it's children can't receive touch events and it takes them instead.

ScouseChris
  • 4,377
  • 32
  • 38
0

Make sure that one of the unintended views inside the page is not consuming click event. I had an issue where android:clickable was true on the image view and I could not get click event from its parent. After removing that android:clickable from image view, the click events are sent to its parent (BannerView) now. See the example code

public ViewPagerAdapter extends PagerAdapter {
...

public Object instantiateItem (ViewGroup container, int position) {

    MyItem item = this.myItems.get(position);

    BannerView bannerView = new BannerView(container.getContext());
    ImageView imageView = (ImageView) bannerView.findViewById(R.id.header_image);

    this.setupClickListener(bannerView, position);

     container.addView(bannerView);
     return bannerView;
}

private void setupClickListener(final BannerView view, final int position) {

    view.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

               // Page is clicked!
               MyItem item = ViewPagerAdapter.this.myItems.get(position);
               ViewPagerAdapter.this.showNextActivity(view.getContext(), item);               
            }
     });

}

}

tmin
  • 1,313
  • 13
  • 15
-2

The proper way to do this is to implement ViewPager.OnPageChangeListener in your Activity. Here is an example:

public class MyActivity implements ViewPager.OnPageChangeListener
{

    private ViewPager mViewPager;
    private int mLastPagePosition = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        mViewPager = (ViewPager) findViewById(R.id.viewpager);
        mViewPager.addOnPageChangeListener(this);

        ...
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (mLastPagePosition != position) {
            // the selected page is changed
            mLastPagePosition = position;
        }
    }

    @Override
    public void onPageSelected(int position) {
        if (mLastPagePosition != position) {
            // the selected page is changed
            mLastPagePosition = position;
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

}
Lee Hounshell
  • 842
  • 9
  • 10