ViewPager dentro de la fila ListView impide que onItemClick sea despedido

Tengo un ViewPager dentro de cada fila de un ListView. Funciona bien, cambia las vistas dentro de él cuando el usuario utiliza el gesto de desplazamiento, pero evita que el método onItemClick de ListView se llame. Sé que el ViewPager es el culpable porque cuando lo oculto, el onItemClick se llama, así que esto es lo que estoy intentando:

He creado un ViewGroup para ser la fila (RowView). Este ViewGroup tiene onInterceptTouchEvent para evitar que ViewPager maneje eventos de toque adicionales cuando detecto un clic. Pero la devolución de llamada onItemClick todavía no se está llamando. Y el selector de lista no se muestra ni en clic. Quiero que estas dos funciones funcionen.

Así es como aparece el objeto onInterceptTouchEvent de RowView:

 @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); } 

¿Alguna sugerencia para resolver esto?

EDIT : No funciona el ejemplo de cómo el onItemClick en MainActivity no se llama cuando el ViewPager está activo (el selector de lista Lollipop tampoco aparece)

Actividad principal

 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); } }); 

Elemento de lista XML:

 <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> 

Adaptador de lista:

 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; } } 

Adaptador de ViewPager:

 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); } } 

Creo que mejor reemplazar listview onintercepter que viewgroup.

TouchEvent Flow simplemente:

Acitividad toque evento -> viewgroup.dispatchtouchevent -> viewgroup.intercepter ..-> view.dispatchtouch … -> …..

En este caso list.dispatch llamada. ViewPager.dispatch evento a ViewPager.dispatch . Pero antes de ViewPager.dispatchtouchevent , llame a ListView.intercepterTouchEvent .

Si dispatchTouchEvent devuelve false llame al TouchEvent View , pero devuelve el descenso true flujo de llamadas.

Si intercepterTouchEvent devuelve true no llama a child dispatchTouchEvent pero devuelve false calling child dispatchTouchEvent . Así que listview.intercepterTouchEvent devuelve true , llamando a nItemClick .

Por lo que si listView.intercepterTouchEvent devuelve true , no viewPager items.

Puede conocer la acción del usuario deslizar o hacer clic en 2 way. TouchEvent y guesturedetector ..

En IntercepterTouchEvent(Event ev); de IntercepterTouchEvent(Event ev); listview 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); } 

Usted puede deslizar la velocidad cerca de 100 o mover la cantidad 100 píxeles. Si no realiza el evento click.

Espero que este texto pueda ayudarte

Y añadir editar algunos golpe de código.

 @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);; } 

Como su papel de opinión es el hijo de su listview, su consumo de los eventos de contacto. Puede evitar esto haciendo que su niño de visores sea unclickable, es decir

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

Sugeriría para fijar OnClickListener en cada instancia de ViewPager sí mismo y evitar el uso de onItemClickListener del ListView. También puede eliminar completamente onInterceptTouchEvent() también. Ésa sería la solución más simple y estable. Menos errores de código;

Necesita realizar una de las siguientes acciones:

Establecer un setOnClickListener en el Viewpager del Viewpager o encontrar

 android:descendantFocusability="beforeDescendants" 

Espero que esto pueda ayudarte.

Creo que esto debería hacer el truco.

 viewPager.getParent().requestDisallowInterceptTouchEvent(false); 

Espero que esto ayude.

La idea es hacer el oyente de clics en el paginador de vista y no en el listView

Al agregar el viewPager en el método getView, configure su etiqueta como la posición de la fila

 holder.viewPager.setTag(position); 

A continuación, agregue el oyente de clics al viewPager (también en 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); } }); 

El problema es con el visor de adaptadores de lista. Tuve un problema similar cuando estaba tratando de implementar la misma característica (viewpager dentro de una fila listview). Lo resolví haciendo esto —- Establezca el onclicklistener en el objeto de visor en lugar de establecer en listview directamente. Para hacer esto debes implementar onitemclicklistener en tu clase de adaptador.

Una mejora sobre la respuesta de Adrian:

 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; } } } 

Esto es lo que he hecho finalmente para manejar esto aunque el selector de lista no funciona. Esto podría mejorarse, pero, por ahora, es la única solución que me gusta cómo funciona.

 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); } } 
  • Android ViewPager, cómo hacer que las vistas de los hermanos sean más pequeñas y más oscuras
  • El fragmento no actualiza el contenido después de volver de la pila posterior
  • Vista fija en el diseño de contenido de Viewpager, bajo CoordinatorLayout
  • ¿Cómo crear ViewPager de carrusel?
  • Escucha de zoom para PhotoView
  • Gestión del ciclo de vida de Android de Fragmentos dentro de ViewPager y FragmentPagerAdapter
  • Android Viewpager como Galería de imágenes de diapositivas
  • Desaparición de los botones de la barra de acción al pasar entre fragmentos
  • Fragment onResume () no se llama cuando se utiliza FragmentPagerAdapter
  • Cómo implementar un ViewPager con diferentes Fragmentos / Layouts
  • Fragmento en ViewPager en Fragmento no se vuelve a cargar en el cambio de orientación
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.