package za.co.immedia.pinnedheaderlistview;
import java.lang.reflect.Method;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
public class PinnedHeaderListView extends ListView implements OnScrollListener {
private final static String TAG = PinnedHeaderListView.class
.getSimpleName();
private OnScrollListener mOnScrollListener;
public static interface PinnedSectionedHeaderAdapter {
public boolean isSectionHeader(int position);
public int getSectionForPosition(int position);
public View getSectionHeaderView(int section, View convertView,
ViewGroup parent);
public int getSectionHeaderViewType(int section);
public int getCount();
public int getGlobalPosition(int section, int position);
public boolean onPinndHeaderTouchEvent(View headerView, int section,
MotionEvent ev);
}
private PinnedSectionedHeaderAdapter mAdapter;
private View mCurrentHeader;
private int mCurrentHeaderViewType = 0;
private float mHeaderOffset;
private boolean mShouldPin = true;
private int mCurrentSection = 0;
private int mWidthMode;
private int mHeightMode;
private Boolean threadStarted = false;
public PinnedHeaderListView(Context context) {
super(context);
super.setOnScrollListener(this);
}
public PinnedHeaderListView(Context context, AttributeSet attrs) {
super(context, attrs);
super.setOnScrollListener(this);
}
public PinnedHeaderListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
super.setOnScrollListener(this);
}
public void setPinHeaders(boolean shouldPin) {
mShouldPin = shouldPin;
}
public boolean hasPinHeader() {
return mShouldPin;
}
public View getCurrentPinHeader() {
return mCurrentHeader;
}
public float getPinHeaderOffset() {
return mHeaderOffset;
}
@Override
public void setAdapter(ListAdapter adapter) {
mCurrentHeader = null;
mAdapter = (PinnedSectionedHeaderAdapter) adapter;
super.setAdapter(adapter);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
if (mAdapter == null || mAdapter.getCount() == 0 || !mShouldPin
|| (firstVisibleItem < getHeaderViewsCount())) {
mCurrentHeader = null;
mHeaderOffset = 0.0f;
for (int i = firstVisibleItem; i < firstVisibleItem
+ visibleItemCount; i++) {
View header = getChildAt(i);
if (header != null) {
header.setVisibility(VISIBLE);
}
}
return;
}
firstVisibleItem -= getHeaderViewsCount();
int section = mAdapter.getSectionForPosition(firstVisibleItem);
int viewType = mAdapter.getSectionHeaderViewType(section);
mCurrentHeader = getSectionHeaderView(section,
mCurrentHeaderViewType != viewType ? null : mCurrentHeader);
ensurePinnedHeaderLayout(mCurrentHeader);
mCurrentHeaderViewType = viewType;
mHeaderOffset = 0.0f;
for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
if (mAdapter.isSectionHeader(i)) {
View header = getChildAt(i - firstVisibleItem);
float headerTop = header.getTop();
float pinnedHeaderHeight = mCurrentHeader.getMeasuredHeight();
header.setVisibility(VISIBLE);
if (pinnedHeaderHeight >= headerTop && headerTop > 0) {
mHeaderOffset = headerTop - header.getHeight();
} else if (headerTop <= 0) {
header.setVisibility(INVISIBLE);
}
}
}
invalidate();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
private View getSectionHeaderView(int section, View oldView) {
boolean shouldLayout = section != mCurrentSection || oldView == null;
View view = mAdapter.getSectionHeaderView(section, oldView, this);
if (shouldLayout) {
// a new section, thus a new header. We should lay it out again
ensurePinnedHeaderLayout(view);
mCurrentSection = section;
}
return view;
}
private void ensurePinnedHeaderLayout(View header) {
if (header.isLayoutRequested()) {
int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
mWidthMode);
int heightSpec;
ViewGroup.LayoutParams layoutParams = header.getLayoutParams();
if (layoutParams != null && layoutParams.height > 0) {
heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height,
MeasureSpec.EXACTLY);
} else {
heightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
header.measure(widthSpec, heightSpec);
header.layout(0, 0, header.getMeasuredWidth(),
header.getMeasuredHeight());
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mAdapter == null || !mShouldPin || mCurrentHeader == null)
return;
int saveCount = canvas.save();
canvas.translate(0, mHeaderOffset);
canvas.clipRect(0, 0, getWidth(), mCurrentHeader.getMeasuredHeight()); // needed
// for
// <
// HONEYCOMB
mCurrentHeader.draw(canvas);
canvas.restoreToCount(saveCount);
}
@Override
public void setOnScrollListener(OnScrollListener l) {
mOnScrollListener = l;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidthMode = MeasureSpec.getMode(widthMeasureSpec);
mHeightMode = MeasureSpec.getMode(heightMeasureSpec);
}
public void setSelection(int section, int position) {
int pos = mAdapter.getGlobalPosition(section, position);
pos += getHeaderViewsCount();
super.setSelection(pos);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mShouldPin && mCurrentHeader != null) {
float x = ev.getX();
float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x <= mCurrentHeader.getWidth()
&& y <= mCurrentHeader.getHeight()
+ getPinHeaderOffset()
&& mAdapter.onPinndHeaderTouchEvent(mCurrentHeader,
mCurrentSection, ev)) {
}
break;
case MotionEvent.ACTION_UP:
if (x <= mCurrentHeader.getWidth()
&& y <= mCurrentHeader.getHeight()
+ getPinHeaderOffset()
&& mAdapter.onPinndHeaderTouchEvent(mCurrentHeader,
mCurrentSection, ev)) {
Log.d(TAG, "onPinHeaderClick....");
Log.d(TAG, "width= " + getCurrentPinHeader().getWidth());
Log.d(TAG, "height = " + getCurrentPinHeader().getHeight());
Log.d(TAG, "pinOffset = " + getPinHeaderOffset());
}
break;
default:
break;
}
}
return super.dispatchTouchEvent(ev);
}
public void setOnItemClickListener(
PinnedHeaderListView.OnItemClickListener listener) {
super.setOnItemClickListener(listener);
}
public static abstract class OnItemClickListener implements
AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> adapterView, View view,
int rawPosition, long id) {
SectionedBaseAdapter adapter;
if (adapterView.getAdapter().getClass()
.equals(HeaderViewListAdapter.class)) {
HeaderViewListAdapter wrapperAdapter = (HeaderViewListAdapter) adapterView
.getAdapter();
adapter = (SectionedBaseAdapter) wrapperAdapter
.getWrappedAdapter();
} else {
adapter = (SectionedBaseAdapter) adapterView.getAdapter();
}
int section = adapter.getSectionForPosition(rawPosition);
int position = adapter.getPositionInSectionForPosition(rawPosition);
if (position == -1) {
onSectionClick(adapterView, view, section, id);
} else {
onItemClick(adapterView, view, section, position, id);
}
}
public abstract void onItemClick(AdapterView<?> adapterView, View view,
int section, int position, long id);
public abstract void onSectionClick(AdapterView<?> adapterView,
View view, int section, long id);
}
}