RecyclerView con columna fija y cabecera, además de pie de página desplazable

Estoy tratando de encontrar una manera de implementar una mesa de pie para una aplicación deportiva (como la NBA Game Time), con un encabezado fijo, una primera columna fija y un pie de página. Busqué un poco en cómo conseguirlo, pero el mejor tiro era este proyecto ( https://github.com/InQBarna/TableFixHeaders ) pero utiliza su propia visión en vez de Recycler de GridView. ¿Alguien sabe algo como esto o sabe cómo puedo empezar con él (Adaptador o LayoutManager)?

Editar (agregar imágenes)

estado inicial

Estado desplazado mostrando pie de página

Después de probar y buscar mucho, implementé por mi cuenta, combinando un ListView con HorizontalScrollView interno.

Primero, extendí HorizontalScrollView para reportarme el evento de desplazamiento, agregando un escucha:

 public class MyHorizontalScrollView extends HorizontalScrollView { private OnScrollListener listener; @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (listener != null) listener.onScroll(this, l, t); } public void setOnScrollListener(OnScrollListener listener) { this.listener = listener; } public interface OnScrollListener { void onScroll(HorizontalScrollView view, int x, int y); } } 

Entonces, he creado mi diseño con un LinearLayout , que contiene mi encabezado y un ListView para mi Activity (o Fragment , si es su necesidad).

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <include android:id="@+id/header" layout="@layout/header" /> <ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> 

Cada elemento de mi lista es un LinearLayout , con un TextView (la columna fija) y un HorizontalScrollView . El diseño del encabezado y las líneas son los siguientes:

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView style="?android:attr/textAppearanceMedium" android:background="#f00" android:minWidth="40dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <View android:layout_width="1px" android:layout_height="match_parent" android:background="@android:color/black" /> <net.rafaeltoledo.example.MyHorizontalScrollView android:id="@+id/scroll" android:scrollbars="none" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal"> <!-- Childs, or columns --> </LinearLayout> </net.rafaeltoledo.example.MyHorizontalScrollView> </LinearLayout> 

El truco consiste en desplazarse todos Con la ayuda de un EventBus (he usado el de GreenRobot ) para disparar el evento de desplazamiento horizontal y mover todos los scrollers como uno solo. Mi objeto de evento contiene los mismos datos de la clase de escucha (¿puedo usar el propio objeto de escucha?)

 public static class Event { private final int x; private final int y; private final HorizontalScrollView view; public Event(HorizontalScrollView view, int x, int y) { this.x = x; this.y = y; this.view = view; } public int getX() { return x; } public int getY() { return y; } public HorizontalScrollView getView() { return view; } } 

La clase de adaptador de lista recibe un oyente para establecer en HorizontalScrollView de cada elemento.

 public static class Adapter extends BaseAdapter { private final Context context; private MyHorizontalScrollView.OnScrollListener listener; public Adapter(Context context, MyHorizontalScrollView.OnScrollListener listener) { this.context = context; this.listener = listener; } @Override public int getCount() { return 30; } @Override public Object getItem(int position) { return new Object(); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.header, parent, false); MyHorizontalScrollView scroll = (MyHorizontalScrollView) convertView.findViewById(R.id.scroll); scroll.setOnScrollListener(listener); } return convertView; } public Context getContext() { return context; } } 

Antes de continuar, MyHorizontalScrollView en EventBus, añadiendo EventBus.getDefault().register(this) a cada versión del constructor, y añadí el método receiver:

 public void onEventMainThread(MainActivity.Event event) { if (!event.getView().equals(this)) scrollTo(event.getX(), event.getY()); } 

Que se desplaza a la posición recibida, si no era el mismo que disparó el evento de desplazamiento.

Y por último, establecí todo en el método onCreate() de mi Activity :

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.list); MyHorizontalScrollView.OnScrollListener listener = new MyHorizontalScrollView.OnScrollListener() { @Override public void onScroll(HorizontalScrollView view, int x, int y) { Log.d("Scroll Event", String.format("Fired! %d %d", x, y)); EventBus.getDefault().post(new Event(view, x, y)); } }; ListView listView = (ListView) findViewById(R.id.list); ViewGroup header = (ViewGroup) findViewById(R.id.header); header.getChildAt(0).setBackgroundColor(Color.WHITE); header.setBackgroundColor(Color.BLUE); ((MyHorizontalScrollView) header.findViewById(R.id.scroll)).setOnScrollListener(listener); listView.setAdapter(new Adapter(this, listener)); listView.addFooterView(getLayoutInflater().inflate(R.layout.footer, listView, false)); } 

(Por favor, ignore algunos colores extraños, es para ver mejor lo que está sucediendo).

Y ta-daa, aquí está el resultado deseado!

resultado final

Puede implementar un diseño similar al siguiente:

 <LinearLayout ... android:orientation="vertical"> <TextView ... /> <com.example.TableHeaderView ... /> <RecyclerView ... /> </LinearLayout> 

Sólo el RecyclerView se desplazará, dejando la vista de texto de título y el encabezado de la tabla en la parte superior de la pantalla.

De sus imágenes sugeriría considerar usar una biblioteca tal como StickyGridHeaders . Extiende GridView y puede tener vistas de cabecera personalizadas.

Las alternativas son StickyListHeaders o HeaderListView pero se centran más en ListView

Editar:

En la investigación adicional, el ejemplo proporcionado en este tutorial parece coincidir con sus requisitos

  • Recyclerview Sin adaptador conectado; Saltar el diseño
  • Desactivar eventos táctiles de RecyclerView (para evitar toques de usuario)?
  • RecyclerView - saltar a la posición, luego desplazamiento suave hasta la parte superior
  • ¿Cómo puede RecyclerView ItemTouchHelper bloqueo deslizar artículo?
  • ¿Cómo configurar la altura de recyclerview de forma programática?
  • CheckBox en RecyclerView mantiene la comprobación de diferentes elementos
  • ListSelector para RecyclerView en el dispositivo táctil libre (usando onKey Listener)
  • RecyclerView: Las clases internas no pueden tener una declaración estática
  • ¿Cómo detectar si RecyclerView está vacío?
  • Confirmación y eliminación de deshacer en RecyclerView
  • RecyclerView gravedad
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.