Skip to content

Instantly share code, notes, and snippets.

@JayNewstrom
Created January 31, 2014 19:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JayNewstrom/8740795 to your computer and use it in GitHub Desktop.
Save JayNewstrom/8740795 to your computer and use it in GitHub Desktop.
A very simple sticky list header helper.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
style="@style/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="6dp"
android:paddingRight="4dp"
android:scrollbarStyle="outsideOverlay"/>
<FrameLayout
android:id="@+id/fl_sticky_header_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@null"/>
</FrameLayout>
public class StickyListActivity extends Activity {
@InjectView(R.id.fl_sticky_header_holder) FrameLayout stickyHeaderHolder;
@InjectView(R.id.list_view) ListView listView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_sticky_list);
Butterknife.inject(this);
StickyHeaderHelper<String> stickyHeaderHelper = new StickyHeaderHelper<String>(
this.listAdapter, this.stickyHeaderHolder, this.listView);
this.listView.setOnScrollListener(stickyHeaderHelper);
this.listAdapter.registerDataSetObserver(stickyHeaderHelper);
}
}
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
/**
* @author jaynewstrom
* Created on 1/28/14.
*/
public class StickyHeaderHelper<S> extends DataSetObserver implements OnScrollListener {
private final StickyHeaderAdapter<S> adapter;
private final ViewGroup stickyHeaderHolder;
private final AbsListView listView;
private S previousSection;
private int previousFirstVisibleItem;
public StickyHeaderHelper(StickyHeaderAdapter<S> adapter, ViewGroup stickyHeaderHolder, AbsListView listView) {
this.adapter = adapter;
this.stickyHeaderHolder = stickyHeaderHolder;
this.listView = listView;
this.reset();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.updateHeaderView(firstVisibleItem);
}
private void updateHeaderView(int firstVisibleItem) {
S section = this.adapter.getSectionForIndex(firstVisibleItem);
if (section != null) {
if (this.previousFirstVisibleItem != firstVisibleItem && firstVisibleItem >= 0) {
if (this.previousSection == null || !this.previousSection.equals(section)) {
View view = this.adapter.getViewForSection(section);
this.stickyHeaderHolder.removeAllViews();
this.stickyHeaderHolder.addView(view);
this.previousSection = section;
}
this.previousFirstVisibleItem = firstVisibleItem;
}
if (firstVisibleItem <= this.listView.getCount()) {
S nextSection = this.adapter.getSectionForIndex(firstVisibleItem + 1);
View header = this.stickyHeaderHolder.getChildAt(0);
if (header != null) {
View firstItem = this.listView.getChildAt(1);
int firstItemTop = firstItem == null ? 0 : firstItem.getTop();
if (section.equals(nextSection)) {
header.setTranslationY(0);
} else {
int transY = Math.min(0, firstItemTop - header.getMeasuredHeight());
header.setTranslationY(transY);
}
View zeroItem = this.listView.getChildAt(0);
if (firstVisibleItem == 0 && zeroItem != null && zeroItem.getTop() == 0) {
this.previousSection = null;
this.previousFirstVisibleItem = -1;
this.stickyHeaderHolder.removeAllViews();
}
}
}
}
}
private void reset() {
this.previousSection = null;
this.previousFirstVisibleItem = -1;
this.stickyHeaderHolder.removeAllViews();
if (this.listView.getCount() > 0) {
this.listView.setSelection(0);
this.updateHeaderView(0);
}
}
@Override
public void onChanged() {
this.reset();
}
@Override
public void onInvalidated() {
this.reset();
}
public interface StickyHeaderAdapter<S> {
public S getSectionForIndex(int index);
public View getViewForSection(S section);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment