/** * Name : PinnedHeaderListView.java * Copyright : Copyright (c) Tencent Inc. All rights reserved. * Description : TODO */ package com.tencent.widget; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; public class PinnedHeaderExpandableListView extends XExpandableListView implements AbsListView.OnScrollListener { public interface OnLayoutListener { public void onLayout(View v, int left, int top, int right, int bottom); } private static final int PINNED_HEADER_STATE_INVISIBLE = -1; private static final int PINNED_HEADER_STATE_PINNED = 0; private static final int PINNED_HEADER_STATE_PUSHING_UP = 1; private Context mContext = null; private ExpandableListAdapter mAdapter = null; private View mHeaderView = null; private boolean mDispatchTouchEventToHeaderView = false; private int mCurHeaderViewState = PINNED_HEADER_STATE_PINNED; private int mCurHeaderViewPushUpDistance = 0; private OnLayoutListener mOnLayoutListener = null; private AbsListView.OnScrollListener mOnScrollListener = null; private OnGroupClickListener mOnGroupClickedListener = null; public static abstract class ExpandableListAdapter extends android.widget.BaseExpandableListAdapter { public abstract int getHeaderViewLayoutResourceId(); public abstract void configHeaderView(View header, int groupPosition); } public PinnedHeaderExpandableListView(Context context) { super(context); init(context); } public PinnedHeaderExpandableListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public PinnedHeaderExpandableListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } @Override public void setAdapter(android.widget.ExpandableListAdapter adapter) { super.setAdapter(adapter); if (adapter instanceof ExpandableListAdapter) { mAdapter = (ExpandableListAdapter) adapter; int headerViewLayoutId = mAdapter.getHeaderViewLayoutResourceId(); if (headerViewLayoutId != 0) { mHeaderView = LayoutInflater.from(mContext).inflate(headerViewLayoutId, this, false); if (mHeaderView != null) { mHeaderView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_UP) { long pos = getExpandableListPosition(getFirstVisiblePosition()); if (getPackedPositionType(pos) == PinnedHeaderExpandableListView.PACKED_POSITION_TYPE_GROUP || getPackedPositionType(pos) == PinnedHeaderExpandableListView.PACKED_POSITION_TYPE_CHILD) { int groupPosition = getPackedPositionGroup(pos); if (mOnGroupClickedListener == null || !mOnGroupClickedListener .onGroupClick( PinnedHeaderExpandableListView.this, v, groupPosition, mAdapter.getGroupId(groupPosition))) { PinnedHeaderExpandableListView.this .collapseGroup(groupPosition); } } } return true; } }); } requestLayout(); } } } public void setOnLayoutListener(OnLayoutListener l) { mOnLayoutListener = l; } @Override public void setOnGroupClickListener(OnGroupClickListener l) { mOnGroupClickedListener = l; super.setOnGroupClickListener(l); } @Override public void setOnScrollListener(AbsListView.OnScrollListener l) { mOnScrollListener = l; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (mOnScrollListener != null) { mOnScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mHeaderView != null) { configHeaderView(firstVisibleItem); } if (mOnScrollListener != null) { mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mHeaderView != null) { measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mHeaderView != null) { configHeaderView(getFirstVisiblePosition()); } if (mOnLayoutListener != null) { mOnLayoutListener.onLayout(this, left, top, right, bottom); } } @Override public boolean dispatchTouchEvent(MotionEvent e) { float x = e.getX(); float y = e.getY(); if (mDispatchTouchEventToHeaderView) { boolean result = mHeaderView != null && mHeaderView.dispatchTouchEvent(e); if (e.getAction() == MotionEvent.ACTION_CANCEL || e.getAction() == MotionEvent.ACTION_UP) { mDispatchTouchEventToHeaderView = false; } return result; } if (mHeaderView != null && x >= 0 && x <= mHeaderView.getMeasuredWidth() && y >= 0 && y <= (mHeaderView.getMeasuredHeight() - mCurHeaderViewPushUpDistance) && e.getAction() == MotionEvent.ACTION_DOWN && mHeaderView.getVisibility() == View.VISIBLE && mHeaderView.dispatchTouchEvent(e)) { mDispatchTouchEventToHeaderView = true; return true; } else { return super.dispatchTouchEvent(e); } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mHeaderView != null && mHeaderView.getVisibility() == View.VISIBLE) { drawChild(canvas, mHeaderView, getDrawingTime()); } } private void init(Context context) { mContext = context; super.setOnScrollListener(this); } private void configHeaderView(int firstVisibleItem) { int firstVisibleItemType = getPackedPositionType(getExpandableListPosition(firstVisibleItem)); if (firstVisibleItemType != PACKED_POSITION_TYPE_NULL) { mAdapter.configHeaderView(mHeaderView, getPackedPositionGroup(getExpandableListPosition(firstVisibleItem))); if (firstVisibleItemType == PACKED_POSITION_TYPE_GROUP) { if (getPackedPositionType(getExpandableListPosition(firstVisibleItem + 1)) == PACKED_POSITION_TYPE_GROUP || getPackedPositionType(getExpandableListPosition(firstVisibleItem + 1)) == PACKED_POSITION_TYPE_NULL) { mCurHeaderViewState = PINNED_HEADER_STATE_INVISIBLE; } else { mCurHeaderViewState = PINNED_HEADER_STATE_PINNED; } } else { View v = getChildAt(0); if (v.getBottom() > mHeaderView.getMeasuredHeight()) { mCurHeaderViewState = PINNED_HEADER_STATE_PINNED; } else { if (getPackedPositionType(getExpandableListPosition(firstVisibleItem + 1)) == PACKED_POSITION_TYPE_GROUP) { mCurHeaderViewState = PINNED_HEADER_STATE_PUSHING_UP; } else { mCurHeaderViewState = PINNED_HEADER_STATE_PINNED; } } } } else { mCurHeaderViewState = PINNED_HEADER_STATE_INVISIBLE; } if (mCurHeaderViewState != PINNED_HEADER_STATE_INVISIBLE) { if (mCurHeaderViewState == PINNED_HEADER_STATE_PUSHING_UP) { View childAtOne = getChildAt(1); mCurHeaderViewPushUpDistance = mHeaderView.getMeasuredHeight() - (childAtOne != null ? childAtOne.getTop() : 0); } else { mCurHeaderViewPushUpDistance = 0; } mHeaderView.setVisibility(View.VISIBLE); int widthSpec = View.MeasureSpec.makeMeasureSpec(mHeaderView.getMeasuredWidth(), View.MeasureSpec.EXACTLY); int heightSpec = View.MeasureSpec.makeMeasureSpec(mHeaderView.getMeasuredHeight(), View.MeasureSpec.EXACTLY); mHeaderView.measure(widthSpec, heightSpec); mHeaderView.layout(0, -mCurHeaderViewPushUpDistance, mHeaderView.getMeasuredWidth(), mHeaderView.getMeasuredHeight() - mCurHeaderViewPushUpDistance); } else { mHeaderView.setVisibility(INVISIBLE); } } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if(mHeaderView != null) { refreshAllChild(mHeaderView); } } private void refreshAllChild(View view) { if(view instanceof ViewGroup) { ViewGroup vg = (ViewGroup) view; for (int i = 0; i < vg.getChildCount(); i++) { refreshAllChild(vg.getChildAt(i)); } } view.refreshDrawableState(); } /** * @return * @author clarkhuang */ public View getHeaderView() { return mHeaderView; } }