Created
January 31, 2014 19:04
-
-
Save JayNewstrom/8740795 to your computer and use it in GitHub Desktop.
A very simple sticky list header helper.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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