content: /**
  The MIT License (MIT)
  
  Copyright (c) 2014 singwhatiwanna
  https://github.com/singwhatiwanna
  http://blog.csdn.net/singwhatiwanna
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
  */
  
  package com.ryg.expandable.ui;
  
  import android.content.Context;
  import android.graphics.Canvas;
  import android.graphics.Rect;
  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.ExpandableListView;
  import android.widget.AbsListView.OnScrollListener;
  
  public class PinnedHeaderExpandableListView extends ExpandableListView implements OnScrollListener {
      private static final String TAG = "PinnedHeaderExpandableListView";
      private static final boolean DEBUG = true;
  
      public interface OnHeaderUpdateListener {
          /**
           * 返回一个view对象即可
           * 注意：view必须要有LayoutParams
           */
          public View getPinnedHeader();
  
          public void updatePinnedHeader(View headerView, int firstVisibleGroupPos);
      }
  
      private View mHeaderView;
      private int mHeaderWidth;
      private int mHeaderHeight;
  
      private View mTouchTarget;
  
      private OnScrollListener mScrollListener;
      private OnHeaderUpdateListener mHeaderUpdateListener;
  
      private boolean mActionDownHappened = false;
      protected boolean mIsHeaderGroupClickable = true;
  
  
      public PinnedHeaderExpandableListView(Context context) {
          super(context);
          initView();
      }
  
      public PinnedHeaderExpandableListView(Context context, AttributeSet attrs) {
          super(context, attrs);
          initView();
      }
  
      public PinnedHeaderExpandableListView(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          initView();
      }
  
      private void initView() {
          setFadingEdgeLength(110);
          setOnScrollListener(this);
      }
  
      @Override
      public void setOnScrollListener(OnScrollListener l) {
          if (l != this) {
              mScrollListener = l;
          } else {
              mScrollListener = null;
          }
          super.setOnScrollListener(this);
      }
  
      /**
       * 给group添加点击事件监听
       * @param onGroupClickListener 监听
       * @param isHeaderGroupClickable 表示header是否可点击<br/>
       * note : 当不想group可点击的时候，需要在OnGroupClickListener#onGroupClick中返回true，
       * 并将isHeaderGroupClickable设为false即可
       */
      public void setOnGroupClickListener(OnGroupClickListener onGroupClickListener, boolean isHeaderGroupClickable) {
          mIsHeaderGroupClickable = isHeaderGroupClickable;
          super.setOnGroupClickListener(onGroupClickListener);
      }
  
      public void setOnHeaderUpdateListener(OnHeaderUpdateListener listener) {
          mHeaderUpdateListener = listener;
          if (listener == null) {
              mHeaderView = null;
              mHeaderWidth = mHeaderHeight = 0;
              return;
          }
          mHeaderView = listener.getPinnedHeader();
          int firstVisiblePos = getFirstVisiblePosition();
          int firstVisibleGroupPos = getPackedPositionGroup(getExpandableListPosition(firstVisiblePos));
          listener.updatePinnedHeader(mHeaderView, firstVisibleGroupPos);
          requestLayout();
          postInvalidate();
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          if (mHeaderView == null) {
              return;
          }
          measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);
          mHeaderWidth = mHeaderView.getMeasuredWidth();
          mHeaderHeight = mHeaderView.getMeasuredHeight();
      }
  
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
          super.onLayout(changed, l, t, r, b);
          if (mHeaderView == null) {
              return;
          }
          int delta = mHeaderView.getTop();
          mHeaderView.layout(0, delta, mHeaderWidth, mHeaderHeight + delta);
      }
  
      @Override
      protected void dispatchDraw(Canvas canvas) {
          super.dispatchDraw(canvas);
          if (mHeaderView != null) {
              drawChild(canvas, mHeaderView, getDrawingTime());
          }
      }
  
      @Override
      public boolean dispatchTouchEvent(MotionEvent ev) {
          int x = (int) ev.getX();
          int y = (int) ev.getY();
          int pos = pointToPosition(x, y);
          if (mHeaderView != null && y >= mHeaderView.getTop() && y <= mHeaderView.getBottom()) {
              if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                  mTouchTarget = getTouchTarget(mHeaderView, x, y);
                  mActionDownHappened = true;
              } else if (ev.getAction() == MotionEvent.ACTION_UP) {
                  View touchTarget = getTouchTarget(mHeaderView, x, y);
                  if (touchTarget == mTouchTarget && mTouchTarget.isClickable()) {
                      //这里没有执行到
                      mTouchTarget.performClick();
                      invalidate(new Rect(0, 0, mHeaderWidth, mHeaderHeight));
                  } else if (mIsHeaderGroupClickable){
                      int groupPosition = getPackedPositionGroup(getExpandableListPosition(pos));
                      if (groupPosition != INVALID_POSITION && mActionDownHappened) {
                          if (isGroupExpanded(groupPosition)) {
                              collapseGroup(groupPosition);
                          } else {
                              expandGroup(groupPosition);
                          }
                      }
                  }
                  mActionDownHappened = false;
              }
              return true;
          }
  
          return super.dispatchTouchEvent(ev);
      }
  
      /**
       *
       * @return 点击在非viewGroup时，直接返回。
       *  点击在viewgroup时：1.如果点击在子view上，返回子view；2.没点击到子view，返回view本身
       *
       */
      private View getTouchTarget(View view, int x, int y) {
          if (!(view instanceof ViewGroup)) {
              return view;
          }
  /*
  getChildDrawingOrder与 isChildrenDrawingOrderEnabled()是属于ViewGroup的方法.
  getChildDrawingOrder 用于 返回当前迭代子视图的索引.就是说 获取当前正在绘制的视图索引.
  如果需要改变ViewGroup子视图绘制的顺序,则需要重载这个方法.并且需要先调用 setChildrenDrawingOrderEnabled(boolean) 方法来启用子视图排序功能.
  isChildrenDrawingOrderEnabled()则是  获取当前这个ViewGroup是否是按照顺序进行绘制的
   */
          ViewGroup parent = (ViewGroup)view;
          int childrenCount = parent.getChildCount();
          final boolean customOrder = isChildrenDrawingOrderEnabled();
          View target = null;
          for (int i = childrenCount - 1; i >= 0; i--) {
              final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
              final View child = parent.getChildAt(childIndex);
              if (isTouchPointInView(child, x, y)) {
                  target = child;
                  break;
              }
          }
          if (target == null) {
              target = parent;
          }
  
          return target;
      }
  
      private boolean isTouchPointInView(View view, int x, int y) {
          if (view.isClickable() && y >= view.getTop() && y <= view.getBottom()
                  && x >= view.getLeft() && x <= view.getRight()) {
              return true;
          }
          return false;
      }
  
     /* public void requestRefreshHeader() {
          refreshHeader();
          invalidate(new Rect(0, 0, mHeaderWidth, mHeaderHeight));
      }*/
  
      protected void refreshHeader() {
          if (mHeaderView == null) {
              return;
          }
          int firstVisiblePos = getFirstVisiblePosition();
          int pos = firstVisiblePos + 1;
          int firstVisibleGroupPos = getPackedPositionGroup(getExpandableListPosition(firstVisiblePos));
          int group = getPackedPositionGroup(getExpandableListPosition(pos));
  //        if (DEBUG) {
  //            Log.d(TAG, "refreshHeader firstVisibleGroupPos=" + firstVisibleGroupPos);
  //        }
          System.out.println("group=group"+group);
          if (group == firstVisibleGroupPos + 1) {//表示在同一组
              View view = getChildAt(1);//本类第二个绘制的是headerView
              if (view == null) {
                  Log.w(TAG, "Warning : refreshHeader getChildAt(1)=null");
                  return;
              }
              if (view.getTop() <= mHeaderHeight) {
                  int delta = mHeaderHeight - view.getTop();
                  mHeaderView.layout(0, -delta, mHeaderWidth, mHeaderHeight - delta);
              } else {
                  //TODO : note it, when cause bug, remove it
                  mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
              }
          } else {
              mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
          }
  
          if (mHeaderUpdateListener != null) {
              mHeaderUpdateListener.updatePinnedHeader(mHeaderView, firstVisibleGroupPos);
          }
      }
  
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
          if (mScrollListener != null) {
              mScrollListener.onScrollStateChanged(view, scrollState);
          }
      }
  
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem,
              int visibleItemCount, int totalItemCount) {
          if (totalItemCount > 0) {
              refreshHeader();
          }
          if (mScrollListener != null) {
              mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
          }
      }
  
  }