I am trying to find a way to implement a standing table for a sports app (like NBA Game Time standings), with a fixed header, fixed first column and a footer. I searched a bit on how to get it, but the best shot was this project (https://github.com/InQBarna/TableFixHeaders) but it uses its own view instead of Recycler of GridView. Do anyone knows something like this or knows how I can start with it (Adapter or LayoutManager)?
Edit (adding images)
After testing and searching a lot, I implemented by my own, combining a ListView
with inner HorizontalScrollView
s.
First, I extended HorizontalScrollView
to report me the scroll event, adding a listener:
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);
}
}
Then, I created my layout with a LinearLayout
, containing my header and a ListView
for my Activity
(or Fragment
, if it's your need).
<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>
Each item of my list is a LinearLayout
, with a TextView
(the fixed column) and a HorizontalScrollView
. The layout of both header and lines are the following:
<?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>
The trick is to scroll all With a help of a EventBus
(I used the GreenRobot's one) to fire the horizontal scroll event and move all scrollers as one. My event object contains the same data from the listener class (maybe I can use the listener object itself?)
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;
}
}
The list adapter class receives a listener to set in the HorizontalScrollView
of each item.
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;
}
}
Before continue, I registered MyHorizontalScrollView
to EventBus, adding EventBus.getDefault().register(this)
to each version of constructor, and added the receiver method to it:
public void onEventMainThread(MainActivity.Event event) {
if (!event.getView().equals(this)) scrollTo(event.getX(), event.getY());
}
that will scroll to the received position, if was not itself that fired the scroll event.
And finally, I setted up everything in the onCreate()
method of my 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));
}
(Please ignore some weird coloring, it's for better viewing what's happening).
And ta-daa, here is the desired result!
You can implement a layout like that like this:
<LinearLayout
...
android:orientation="vertical">
<TextView
... />
<com.example.TableHeaderView
... />
<RecyclerView
... />
</LinearLayout>
Only the RecyclerView will scroll, leaving your title text view and table header at the top of the screen.
From your images I would suggest considering using a library such as StickyGridHeaders. It extends GridView
and can have custom 'header' views.
Alternatives are StickyListHeaders or HeaderListView but focus more on the ListView
Edit:
On further investigation, the example provided in this tutorial seems to match your requirements
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With