ViewPager inside ListView line prevents onItemClick from starting

I have a ViewPager inside each row of a ListView. It works fine, it changes the views inside it when the user uses swipe gestures, but he forbids calling the ListView onItemClick method. I know that ViewPager is the culprit because when I hide it, onItemClick is called, so I try:

I created a ViewGroup as a row (RowView). This ViewGroup has an onInterceptTouchEvent to avoid viewing the ViewPager for subsequent touches when I detect a click. But the onItemClick callback is still not being called. The list selector is not displayed with a single click. I want these two functions to work.

Here's what the onInterceptTouchEvent from the RowView looks like:

 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int actionMasked = ev.getActionMasked(); switch(actionMasked) { case MotionEvent.ACTION_DOWN: Log.d("RowView", "OnInterceptTouchEvent - Down"); tapDetector.onTouchEvent(ev); return false; case MotionEvent.ACTION_CANCEL: Log.d("RowView", "OnInterceptTouchEvent - Cancel"); tapDetector.onTouchEvent(ev); break; case MotionEvent.ACTION_UP: if(tapDetector.onTouchEvent(ev)) { Log.d("RowView", "OnInterceptTouchEvent - UP!"); return true; } break; } return super.onInterceptTouchEvent(ev); } 

Any suggestions to solve this problem?

EDIT . An example of how the onItemClick in MainActivity is not called when the ViewPager is active does not work (the Lollipop list selector also does not appear)

Mainactivity

 ListView listView = (ListView) findViewById(R.id.main_list); listView.setAdapter(new MainListAdapter(this, 30)); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d(TAG, "onItemClick: " + position); } }); 

XML List Element:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="80dp" android:descendantFocusability="blocksDescendants" > <TextView android:id="@+id/row_num" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> <android.support.v4.view.ViewPager android:id="@+id/row_viewpager" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="visible" /> </RelativeLayout> 

Adapters list:

 public class MainListAdapter extends BaseAdapter { private Context context; private LayoutInflater inflater; private int count; public MainListAdapter(Context context, int count) { this.context = context; this.inflater = LayoutInflater.from(context); this.count = count; } @Override public int getCount() { return count; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if(convertView == null) { holder = new ViewHolder(); convertView = createRow(parent, holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.textView.setText(Integer.toString(position)); int randomPages = (int) (new Random().nextDouble()*5+2); holder.viewPager.setAdapter(new NumAdapter(context, randomPages)); return convertView; } private View createRow(ViewGroup parent, ViewHolder holder) { View view = inflater.inflate(R.layout.row_main_listview, parent, false); holder.textView = (TextView) view.findViewById(R.id.row_num); holder.viewPager = (ViewPager) view.findViewById(R.id.row_viewpager); view.setTag(holder); return view; } private static class ViewHolder { public TextView textView; public ViewPager viewPager; } } 

ViewPager Adapter:

 public class NumAdapter extends PagerAdapter { private LayoutInflater inflater; private int count; public NumAdapter(Context context, int count) { this.inflater = LayoutInflater.from(context); this.count = count; } @Override public Object instantiateItem(ViewGroup container, int position) { TextView textView = (TextView) inflater.inflate(R.layout.page_viewpager, container, false); textView.setText("Page " + position); container.addView(textView); return textView; } @Override public int getCount() { return count; } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View)object); } } 
+6
source share
9 answers

This is what I finally did to manage this, although the list selector does not work. This can be improved, but at the moment this is the only workaround that I like the way it works.

 public class CustomListView extends ListView implements AbsListView.OnScrollListener { /* * Used for detect taps */ private GestureDetector tapDetector; /* * As we need to set our own OnScrollListener, this stores the one * used outside, if any */ private OnScrollListener onScrollListener; private boolean isScrolling = false; public CustomListView(Context context) { super(context); initView(context); } public CustomListView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CustomListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initView(context); } private void initView(Context context) { tapDetector = new GestureDetector(context, new TapListener()); super.setOnScrollListener(this); } @Override public void setOnScrollListener(OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { boolean isTap = tapDetector.onTouchEvent(ev); if(ev.getActionMasked() == MotionEvent.ACTION_UP) { // Don't perform the click if the ListView is scrolling // so it is able to stop the scroll if(isTap && !isScrolling && hasOnItemClickListener()) { int itemPosition = pointToPosition((int)ev.getX(), (int)ev.getY()); performItemClick(this, itemPosition, getItemIdAtPosition(itemPosition)); } } return super.dispatchTouchEvent(ev); } public boolean hasOnItemClickListener() { return getOnItemClickListener() != null; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { isScrolling = scrollState != OnScrollListener.SCROLL_STATE_IDLE; if(this.onScrollListener != null) { onScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if(this.onScrollListener != null) { onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } 
0
source

I think it's better to override listview onintercepter than viewgroup.

TouchEvent Flow is simple:

Touch touch event -> viewgroup.dispatchtouchevent -> viewgroup.intercepter ..-> view.dispatchtouch ... → .....

in this case, call list.dispatch. enter the ViewPager.dispatch event. but before ViewPager.dispatchtouchevent call ListView.intercepterTouchEvent .

if dispatchTouchEvent return false call the parent View TouchEvent , but return true call flow descent.

if intercepterTouchEvent return true does not call child dispatchTouchEvent , but returns a false call to child dispatchTouchEvent . therefore ListView.intercepterTouchEvent return true by calling o nItemClick .

therefore, if ListView.intercepterTouchEvent returns true , and not pull out the viewPager elements.

you can find out how the user action scrolls or click 2 ways. TouchEvent and guesturedetector ..

in the list of IntercepterTouchEvent(Event ev);

 VelocityTracker mVelocityTracker; PointF mLastPoint; public mListView(Context context) { super(context); init(); } public mListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public mListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init(){ mLastPoint = new PointF(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); switch (ev.getAction()){ case MotionEvent.ACTION_MOVE: mVelocityTracker.computeCurrentVelocity(100); int x = (int)Math.abs(mVelocityTracker.getXVelocity()); int move_x = (int)Math.abs(ev.getX() - mLastPoint.x); Log.d("ListView","speed : " + x +" move_x : " + move_x); //here x is drag speed. (pixel/s) // change value left right both value you want speed and move amount if(move_x < 100 || x <100) { mLastPoint.set(ev.getX(), ev.getY()); return true; } break; case MotionEvent.ACTION_DOWN: mLastPoint.set(ev.getX(), ev.getY()); break; case MotionEvent.ACTION_UP: mVelocityTracker.recycle();mVelocityTracker = null; break; } return super.onInterceptTouchEvent(ev); } 

You can scroll speed around 100 or move the number of 100 pixels. if the click event is not executed.

Hope this text helps you ...

and add the edited beat code.

 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); switch (ev.getAction()){ case MotionEvent.ACTION_MOVE: mVelocityTracker.computeCurrentVelocity(10); int x = (int)Math.abs(mVelocityTracker.getXVelocity()); int move_x = (int)Math.abs(ev.getX() - mLastPoint.x); int move_y = (int)Math.abs(ev.getY() - mLastPoint.y); Log.d("ListView","speed : " + x +" move_x : " + move_x + " move_y : "+ move_y); if(move_x < move_y || x < 10) { mLastPoint.set(ev.getX(), ev.getY()); return true; }else if(move_x > move_y){ return false; } break; case MotionEvent.ACTION_DOWN: mLastPoint.set(ev.getX(), ev.getY()); break; case MotionEvent.ACTION_UP: break; } return super.onInterceptTouchEvent(ev); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.d("ListView", "dispatch"); switch (ev.getAction()){ case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_UP: if(mVelocityTracker != null){mVelocityTracker.recycle();mVelocityTracker = null;} break; } return super.dispatchTouchEvent(ev);; } 
+5
source

Since your newspaper is your child on the list, it consumes touch events. You can prevent this by making your viewer a child inconspicuous, i.e.

  TextView textView =(TextView)inflater.inflate(R.layout.page_viewpager,container, false); textView.setText("Page " + position); textView.setClickable(false); 
+3
source

I would suggest setting OnClickListener for each instance of ViewPager and avoiding using onItemClickListener in ListView. Then you can completely remove onInterceptTouchEvent() . This would be the easiest and most stable solution. Fewer errors without code;)

+3
source

You need to do one of the following:

Set a setOnClickListener for the child of the Viewpager or find

 android:descendantFocusability="beforeDescendants" 

Hope this can help you.

+2
source

I think this should do the trick.

 viewPager.getParent().requestDisallowInterceptTouchEvent(false); 

Hope this helps you.

+2
source

The idea is to make a click listener on the view pager, not a listView

When adding viewPager to getView method, set its tag as line position

 holder.viewPager.setTag(position); 

Then add a click listener in viewPager (also in getView)

 viewPager.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { int position = v.getTag(); Log.d(TAG, "onItemClick: " + position); //Fire a delegate or notification of whatever you want to do on the item click. You now have the position myClickListener.onItemClicked(position); } }); 
+2
source

The problem is with the list adapter list owner. I had a similar problem when I tried to implement the same function (viewpager inside the list line). I solved this by doing this ---- Install onclicklistener on the viewholder object, not directly in the list. To do this, you need to implement onitemclicklistener in your adapter class.

+2
source

Improving Adrian's answer:

 public class CustomListView extends ListView { private GestureDetector gestureDetector; public CustomListView(Context context) { super(context); gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener()); } public CustomListView(Context context, AttributeSet attrs) { super(context, attrs); gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener()); } public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener()); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CustomListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener()); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { gestureDetector.onTouchEvent(ev); return super.dispatchTouchEvent(ev); } private class CustomListViewOnGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent ev) { int firstVisiblePosition = getFirstVisiblePosition(); int itemPosition = pointToPosition((int) ev.getX(), (int) ev.getY()); int childIndex = itemPosition - firstVisiblePosition; View view = getChildAt(childIndex); performItemClick(view, itemPosition, getItemIdAtPosition(itemPosition)); return true; } } } 
+1
source

Source: https://habr.com/ru/post/985101/


All Articles