package com.marshalchen.common.ui;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.GridView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ListView.FixedViewInfo;
public class HeaderGridView extends GridView implements OnScrollListener{
private ListAdapter mAdapter;
private boolean mShowHeader = false;
private Context mContext;
private int mScrollOfsset;
private int initialTopPadding;
private int mDisplayWidth = 0;
public HeaderGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public HeaderGridView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public HeaderGridView(Context context) {
super(context);
init(context);
}
private void init(Context context){
mContext = context;
super.setOnScrollListener(this);
}
/**
* A class that represents a fixed view in a list, for example a header at the top
* or a footer at the bottom.
*/
private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();
// TODO: add in footer view support. pull request someone?
private ArrayList<FixedViewInfo> mFooterViewInfos = new ArrayList<FixedViewInfo>();
private void notifiyChanged(){
this.requestLayout();
this.invalidate();
}
public void addHeaderView(View v, Object data, boolean isSelectable) {
FixedViewInfo info = new ListView(getContext()).new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mHeaderViewInfos.add(info);
setupView(v);
int topPadding = this.getPaddingTop();
if(initialTopPadding == 0){
initialTopPadding = topPadding;
}
this.setPadding(this.getPaddingLeft(), topPadding+v.getMeasuredHeight(), this.getPaddingRight(), this.getPaddingBottom());
// in the case of re-adding a header view, or adding one later on,
// we need to notify the observer
this.notifiyChanged();
}
private void setupView(View v){
// in my application the views are merely just inflated... because of this measure and layout need to be called
boolean isLayedOut = !((v.getRight()==0) && (v.getLeft()==0) && (v.getTop()==0) && (v.getBottom()==0));
// no need to do this more than nce per view. Sometimes people repeat headers.
if(v.getMeasuredHeight() != 0 && isLayedOut ) return;
if(mDisplayWidth == 0){
DisplayMetrics displaymetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
mDisplayWidth = displaymetrics.widthPixels;
}
v.measure(MeasureSpec.makeMeasureSpec(mDisplayWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
v.layout(0, getTotalHeaderHeight(), v.getMeasuredWidth(), getTotalHeaderHeight() + v.getMeasuredHeight());
}
public void addHeaderView(View v) {
this.addHeaderView(v, null, false);
}
private void drawHeaders(Canvas canvas) {
int startPos = -mScrollOfsset; // translate view all way up first...
int saveCount = canvas.save();
for(FixedViewInfo header: mHeaderViewInfos){
View view = header.view;
canvas.translate(0, startPos);
startPos = view.getMeasuredHeight();
view.draw(canvas);
}
canvas.restoreToCount(saveCount);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
drawHeaders(canvas);
}
private void invalidateIfAnimating() {
invalidate();
}
public int getHeaderViewCount(){
return mHeaderViewInfos.size();
}
private int getTotalHeaderHeight(){
int totalHeaderHeight = 0;
for(FixedViewInfo h: mHeaderViewInfos){
totalHeaderHeight += h.view.getMeasuredHeight();
}
return totalHeaderHeight;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(this.getAdapter()!=null){
int count = this.getChildCount();
int totalHeaderHeight = getTotalHeaderHeight();
if(count > 0 && firstVisibleItem == 0){
View child = this.getChildAt(0);
mScrollOfsset = totalHeaderHeight - child.getTop() + initialTopPadding;
// need to check to see if should show header
// no need to dispatch otherwise
if(child.getTop() >= 0){
mShowHeader = true;
}else{
mShowHeader = false;
}
}else{
mShowHeader = false;
}
if(mShowHeader){
this.invalidateIfAnimating();
}
}
}
/**
* Remove the view and return true is a view has been removed.
* @param v
* @return
*/
public boolean removeHeaderView(View v) {
if (mHeaderViewInfos.size() > 0) {
return removeFixedViewInfo(v, mHeaderViewInfos);
}
return false;
}
private boolean removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {
int len = where.size();
int count = 0;
for (int i = 0; i < len; ++i) {
FixedViewInfo info = where.get(i);
if (info.view == v) {
this.setPadding(this.getPaddingLeft(), getPaddingTop()-v.getMeasuredHeight(), this.getPaddingRight(), this.getPaddingBottom());
where.remove(i);
count++;
break;
}
}
return count > 0;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
}