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.
- Android.content.res.Resources $ NotFoundException: No se puede encontrar el recurso ID # 0xffffffff
- De error de memoria durante el uso de viewpager + imageview incluso cuando se utiliza la memoria caché y asynctask
- Problema con ViewPager y PageTransformer con la API de Android <11
- ¿Cómo implemento el mecanismo de reciclado de la vista para PagerAdapter?
- Añadir fragmento en un fragmento (fragmento anidado)
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); } }
- Fragmento Reemplazar con ViewPager en la pestaña ActionBar
- ¿Las pestañas anidadas con el visor de desplazamiento en android?
- Widget de página de Viewpager más ancho de pantalla en Android
- ViewPager y base de datos
- Quitar y agregar la página a FragmentPagerAdapter
- cómo extender el WebView correctamente?
- Fragmento de Viewpager anidado no inicializado
- Android AppBarLayout empuja ViewPager hacia abajo
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); } }
- ¿Cómo eliminar una vista animada después de que termine la animación en Android?
- Android: alinear el centro LinearLayout para todos los tamaños de pantalla