Androide. Desplazamiento de 2 vistas de lista

DE ACUERDO. Lo que estoy tratando de lograr es un diseño que hace el mismo efecto que los paneles congelados en Excel. Eso es que quiero una fila de cabecera que se desplaza horizontalmente con el ListView principal y un ListView de mano izquierda que se desplaza verticalmente con el ListView principal. La fila de encabezado y la vista de lista izquierda deben permanecer estacionarias al desplazarse en la otra dimensión.

Aquí está el diseño xml:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recordViewLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <LinearLayout android:layout_width="160dp" android:layout_height="match_parent" android:orientation="vertical"> <CheckBox android:id="@+id/checkBoxTop" android:text="Check All" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ListView android:id="@+id/engNameList" android:layout_width="160dp" android:layout_height="wrap_content"/> </LinearLayout> <HorizontalScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:id="@+id/scroll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <include layout="@layout/record_view_line" android:id="@+id/titleLine" /> <ListView android:id="@android:id/list" android:layout_height="wrap_content" android:layout_width="match_parent"/> </LinearLayout> </HorizontalScrollView> </LinearLayout> 

A continuación, utilizo este código en ListActivity

 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { View v = recordsListView.getChildAt(0); int top = (v == null) ? 0 : v.getTop(); ((ListView)findViewById(R.id.engNameList)).setSelectionFromTop(firstVisibleItem, top); } 

Esto debería hacer que la izquierda de ListView para desplazarse cuando la mano derecha es desplazado por el usuario. Desafortunadamente no lo hace.

He tenido un poco de un google y parece que la función setSelectionFromTop () no funcionará en un ListView que está anidado dentro de más de un diseño.

Si este es el caso, ¿alguien puede sugerir una manera de hacer que se desplacen juntos o una manera diferente de configurar el diseño o una técnica diferente por completo.

Volver a escribir

No tuve mucha suerte con pasar las acciones de desplazamiento en un ListView a otro. Así que elegí un método diferente: pasar el MotionEvent . Esto permite que cada ListView calcule su propio desplazamiento suave, desplazamiento rápido o cualquier otra cosa.

Primero, necesitaremos algunas variables de clase:

 ListView listView; ListView listView2; View clickSource; View touchSource; int offset = 0; 

Cada método que agrego a listView será casi idéntico para listView2 , la única diferencia es que listView2 hará referencia a listView (no a sí mismo). No listView2 código repetitivo listView2 .

En segundo lugar, comencemos con OnTouchListener:

 listView = (ListView) findViewById(R.id.engNameList); listView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if(touchSource == null) touchSource = v; if(v == touchSource) { listView2.dispatchTouchEvent(event); if(event.getAction() == MotionEvent.ACTION_UP) { clickSource = v; touchSource = null; } } return false; } }); 

Para evitar la lógica circular: listView llamadas listView2 llamadas listView llamadas … He utilizado una variable de clase touchSource para determinar cuando un MotionEvent debe pasar. Asumí que usted no quiere una fila chascar listView para también chascar listView2 , así que utilicé otra variable de la clase clickSource para prevenir esto.

En tercer lugar, el OnItemClickListener:

 listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if(parent == clickSource) { // Do something with the ListView was clicked } } }); 

Cuarto, pasar cada evento táctil no es perfecto porque aparecen discrepancias ocasionales. El OnScrollListener es perfecto para eliminar estos:

 listView.setOnScrollListener(new OnScrollListener() { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if(view == clickSource) listView2.setSelectionFromTop(firstVisibleItem, view.getChildAt(0).getTop() + offset); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) {} }); 

(Opcional) Por último, mencionó que tiene problemas ya que listView y listView2 comienzan en diferentes alturas en su diseño … Recomiendo ampliamente modificar su diseño para equilibrar los ListViews, pero encontré una forma de abordar esto. Sin embargo, es un poco difícil.
No puedes calcular la diferencia de altura entre los dos diseños hasta que se haya procesado todo el diseño, pero no hay devolución de llamada para este momento … así que utilizo un manejador simple:

 Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // Set listView's x, y coordinates in loc[0], loc[1] int[] loc = new int[2]; listView.getLocationInWindow(loc); // Save listView's y and get listView2's coordinates int firstY = loc[1]; listView2.getLocationInWindow(loc); offset = firstY - loc[1]; //Log.v("Example", "offset: " + offset + " = " + firstY + " + " + loc[1]); } }; 

Supongo que un retraso de medio segundo es lo suficientemente largo como para procesar el diseño e iniciar el temporizador en onResume() :

 handler.sendEmptyMessageDelayed(0, 500); 

Si utiliza un desplazamiento, quiero dejar claro que el método OnScroll de listView2 resta el desplazamiento en lugar de añadirlo:

 listView2.setSelectionFromTop(firstVisibleItem, view.getChildAt(0).getTop() - offset); 

¡Espero que ayude!

Este hilo me ha ayudado a encontrar una solución para un problema similar con el que he estado luchando durante un tiempo. También se basa en la interceptación de eventos táctiles. En este caso, la sincronización funciona para múltiples vistas de lista y es totalmente simétrica.

El reto principal fue evitar que los clics de elementos de la lista se propagaran a las otras vistas de lista. Necesito clics y clics largos enviados sólo por el listview que recibió inicialmente el evento táctil, incluyendo en particular la realimentación de realce cuando simplemente tocas un elemento (interceptar evento onClick no sirve ya que es demasiado tarde en la jerarquía de llamadas).

La clave de esto es interceptar el evento táctil dos veces. En primer lugar, el evento táctil inicial se retransmite a las otras vistas de lista. La misma función de controlador onTouch captura estos y los alimenta a un GestureDetector. En las GestureDetector llamada de GestureDetector los eventos de tacto estático ( onDown etc.) ( return true ) mientras que los gestos de movimiento no son ( return false ) de tal manera que puedan ser enviados por la vista misma y activar el desplazamiento.

El uso de setPositionFromTop dentro de onScroll no funcionó para mí porque hace que el comportamiento de desplazamiento sea extremadamente lento. OnScroll se utiliza en su lugar para alinear las posiciones de desplazamiento inicial cuando se agregan nuevas ListViews al Syncer.

El único problema que persiste hasta ahora es el planteado por s1ni5t3r arriba. Si doblas el listView, entonces se desconectarán.

 public class ListScrollSyncer implements AbsListView.OnScrollListener, OnTouchListener, OnGestureListener { private GestureDetector gestureDetector; private Set<ListView> listSet = new HashSet<ListView>(); private ListView currentTouchSource; private int currentOffset = 0; private int currentPosition = 0; public void addList(ListView list) { listSet.add(list); list.setOnTouchListener(this); list.setSelectionFromTop(currentPosition, currentOffset); if (gestureDetector == null) gestureDetector = new GestureDetector(list.getContext(), this); } public void removeList(ListView list) { listSet.remove(list); } public boolean onTouch(View view, MotionEvent event) { ListView list = (ListView) view; if (currentTouchSource != null) { list.setOnScrollListener(null); return gestureDetector.onTouchEvent(event); } else { list.setOnScrollListener(this); currentTouchSource = list; for (ListView list : listSet) if (list != currentTouchSource) list.dispatchTouchEvent(event); currentTouchSource = null; return false; } } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (view.getChildCount() > 0) { currentPosition = view.getFirstVisiblePosition(); currentOffset = view.getChildAt(0).getTop(); } } public void onScrollStateChanged(AbsListView view, int scrollState) { } // GestureDetector callbacks public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } public boolean onSingleTapUp(MotionEvent e) { return true; } public boolean onDown(MotionEvent e) { return true; } public void onLongPress(MotionEvent e) { } public void onShowPress(MotionEvent e) { } } 

Edit: el problema de doble fling se puede resolver usando una variable de estado.

 private boolean scrolling; public boolean onDown(MotionEvent e) { return !scrolling; } public void onScrollStateChanged(AbsListView view, int scrollState) { scrolling = scrollState != SCROLL_STATE_IDLE; } 

DE ACUERDO. Tengo una respuesta ahora. El problema es que .setSelectionFromTop () sólo funcionaría si el listview estuviera en el diseño superior (es decir, no anidado). Después de un poco de rascarse la cabeza me di cuenta de que podía hacer mi diseño un RelativeLayout y obtener el mismo aspecto, pero sin tener que anidar los diseños de la casilla de verificación y listview. Este es el nuevo diseño:

  <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recordViewLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <CheckBox android:id="@+id/checkBoxTop" android:text="Check All" android:layout_width="160dp" android:layout_height="wrap_content"/>" <ListView android:id="@+id/engNameList" android:layout_width="160dp" android:layout_height="wrap_content" android:layout_below="@+id/checkBoxTop"/> <HorizontalScrollView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/checkBoxTop"> <LinearLayout android:id="@+id/scroll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <include layout="@layout/record_view_line" android:id="@+id/titleLine" /> <ListView android:id="@android:id/list" android:layout_height="wrap_content" android:layout_width="match_parent"/> </LinearLayout> </HorizontalScrollView> </RelativeLayout> 

Esto básicamente es el código que va con el diseño.

En onCreate ()

  engListView=getListView(); engListView.setOnTouchListener(this); recordListView=(ListView)findViewById(R.id.recordList); recordListView.setOnScrollListener(this); 

Y los métodos de escucha:

 public boolean onTouch(View arg0, MotionEvent event) { recordListView.dispatchTouchEvent(event); return false; } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { View v=view.getChildAt(0); if(v != null) engListView.setSelectionFromTop(firstVisibleItem, v.getTop()); } 

Lo resuelvo cambiando ListView a RecyclerView, uso RecyclerView .addOnScrollListener para sincronizar el evento de desplazamiento. Y no hay discrepancias, es perfecto!

 private void syncScrollEvent(RecyclerView leftList, RecyclerView rightList) { leftList.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return rightList.getScrollState() != RecyclerView.SCROLL_STATE_IDLE; } }); rightList.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return leftList.getScrollState() != RecyclerView.SCROLL_STATE_IDLE; } }); leftList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { rightList.scrollBy(dx, dy); } } }); rightList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { leftList.scrollBy(dx, dy); } } }); } 

De acuerdo con las instrucciones de Sam, desarrollé una aplicación sencilla mostrando desplazamiento de dos vistas en pantalla. En realidad, mi propósito principal es crear un listview expandible horizontalmente desplazable con columnas fijas / congeladas y trabajando en él cuando termine, lo compartiré pronto https://github.com/huseyinDaVinci/Android/tree/master/FrozenListViews

Prueba abajo solución que funciona en mi caso

 leftlist.setOnScrollListener(new SyncedScrollListener(rightlist)); rightlist.setOnScrollListener(new SyncedScrollListener(leftlist)); 

SyncedScrollListener.java

 package com.xorbix.util; import android.view.View; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; public class SyncedScrollListener implements OnScrollListener{ int offset; int oldVisibleItem = -1; int currentHeight; int prevHeight; private View mSyncedView; public SyncedScrollListener(View syncedView){ if(syncedView == null){ throw new IllegalArgumentException("syncedView is null"); } mSyncedView = syncedView; } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { int[] location = new int[2]; if(visibleItemCount == 0){ return; } if(oldVisibleItem != firstVisibleItem){ if(oldVisibleItem < firstVisibleItem){ prevHeight = currentHeight; currentHeight = view.getChildAt(0).getHeight(); offset += prevHeight; }else{ currentHeight = view.getChildAt(0).getHeight(); View prevView; if((prevView = view.getChildAt(firstVisibleItem - 1)) != null){ prevHeight = prevView.getHeight(); }else{ prevHeight = 0; } offset -= currentHeight; } oldVisibleItem = firstVisibleItem; } view.getLocationOnScreen(location); int listContainerPosition = location[1]; view.getChildAt(0).getLocationOnScreen(location); int currentLocation = location[1]; int blah = listContainerPosition - currentLocation + offset; mSyncedView.scrollTo(0, blah); } public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO Auto-generated method stub } } 
  • Agregar una vista de pie de página nativa a la vista web
  • ¿Efecto de desplazamiento personalizado para scrollview?
  • ¿Cómo hacer el desplazamiento de ListView más lento cuando se utiliza la función smoothScrollToPosition ()?
  • Vista de texto no desplazable en android
  • Bootstrap 3 largo desplazamiento modal de fondo en Android
  • ¿Cómo obtener las flechas derecha / izquierda en Actividad para indicar más pantallas a la derecha / izquierda?
  • Android scroll down
  • Touchend no dispara después de touchmove
  • El desplazamiento suave no funciona en ViewPager (biblioteca de soporte)
  • Cómo detectar Android ListView Scrolling se detuvo?
  • GridView con muchas imágenes da como resultado un desplazamiento lento / áspero
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.