public class MainActivity extends Activity implements ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupClickListener, OnHeaderUpdateListener, OnGiveUpTouchEventListener { private PinnedHeaderExpandableListView expandableListView; private StickyLayout stickyLayout; private ArrayList<Group> groupList; private ArrayList<List<People>> childList; private MyexpandableListAdapter adapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); expandableListView = (PinnedHeaderExpandableListView) findViewById(R.id.expandablelist); stickyLayout = (StickyLayout)findViewById(R.id.sticky_layout); initData(); adapter = new MyexpandableListAdapter(this); expandableListView.setAdapter(adapter); // 展开所有group for (int i = 0, count = expandableListView.getCount(); i < count; i++) { expandableListView.expandGroup(i); } expandableListView.setOnHeaderUpdateListener(this); expandableListView.setOnChildClickListener(this); expandableListView.setOnGroupClickListener(this); stickyLayout.setOnGiveUpTouchEventListener(this); } /*** * InitData */ void initData() { groupList = new ArrayList<Group>(); Group group = null; for (int i = 0; i < 3; i++) { group = new Group(); group.setTitle("group-" + i); groupList.add(group); } childList = new ArrayList<List<People>>(); for (int i = 0; i < groupList.size(); i++) { ArrayList<People> childTemp; if (i == 0) { childTemp = new ArrayList<People>(); for (int j = 0; j < 13; j++) { People people = new People(); people.setName("yy-" + j); people.setAge(30); people.setAddress("sh-" + j); childTemp.add(people); } } else if (i == 1) { childTemp = new ArrayList<People>(); for (int j = 0; j < 8; j++) { People people = new People(); people.setName("ff-" + j); people.setAge(40); people.setAddress("sh-" + j); childTemp.add(people); } } else { childTemp = new ArrayList<People>(); for (int j = 0; j < 23; j++) { People people = new People(); people.setName("hh-" + j); people.setAge(20); people.setAddress("sh-" + j); childTemp.add(people); } } childList.add(childTemp); } } /*** * 数据源 * * @author Administrator * */ class MyexpandableListAdapter extends BaseExpandableListAdapter { private Context context; private LayoutInflater inflater; public MyexpandableListAdapter(Context context) { this.context = context; inflater = LayoutInflater.from(context); } // 返回父列表个数 @Override public int getGroupCount() { return groupList.size(); } // 返回子列表个数 @Override public int getChildrenCount(int groupPosition) { return childList.get(groupPosition).size(); } @Override public Object getGroup(int groupPosition) { return groupList.get(groupPosition); } @Override public Object getChild(int groupPosition, int childPosition) { return childList.get(groupPosition).get(childPosition); } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public boolean hasStableIds() { return true; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { GroupHolder groupHolder = null; if (convertView == null) { groupHolder = new GroupHolder(); convertView = inflater.inflate(R.layout.group, null); groupHolder.textView = (TextView) convertView .findViewById(R.id.group); groupHolder.imageView = (ImageView) convertView .findViewById(R.id.image); convertView.setTag(groupHolder); } else { groupHolder = (GroupHolder) convertView.getTag(); } groupHolder.textView.setText(((Group) getGroup(groupPosition)) .getTitle()); if (isExpanded)// ture is Expanded or false is not isExpanded groupHolder.imageView.setImageResource(R.drawable.expanded); else groupHolder.imageView.setImageResource(R.drawable.collapse); return convertView; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { ChildHolder childHolder = null; if (convertView == null) { childHolder = new ChildHolder(); convertView = inflater.inflate(R.layout.child, null); childHolder.textName = (TextView) convertView .findViewById(R.id.name); childHolder.textAge = (TextView) convertView .findViewById(R.id.age); childHolder.textAddress = (TextView) convertView .findViewById(R.id.address); childHolder.imageView = (ImageView) convertView .findViewById(R.id.image); Button button = (Button) convertView .findViewById(R.id.button1); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "clicked pos=", Toast.LENGTH_SHORT).show(); } }); convertView.setTag(childHolder); } else { childHolder = (ChildHolder) convertView.getTag(); } childHolder.textName.setText(((People) getChild(groupPosition, childPosition)).getName()); childHolder.textAge.setText(String.valueOf(((People) getChild( groupPosition, childPosition)).getAge())); childHolder.textAddress.setText(((People) getChild(groupPosition, childPosition)).getAddress()); return convertView; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } } @Override public boolean onGroupClick(final ExpandableListView parent, final View v, int groupPosition, final long id) { return false; } @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { Toast.makeText(MainActivity.this, childList.get(groupPosition).get(childPosition).getName(), 1) .show(); return false; } class GroupHolder { TextView textView; ImageView imageView; } class ChildHolder { TextView textName; TextView textAge; TextView textAddress; ImageView imageView; } @Override public View getPinnedHeader() { View headerView = (ViewGroup) getLayoutInflater().inflate(R.layout.group, null); headerView.setLayoutParams(new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); return headerView; } @Override public void updatePinnedHeader(View headerView, int firstVisibleGroupPos) { Group firstVisibleGroup = (Group) adapter.getGroup(firstVisibleGroupPos); TextView textView = (TextView) headerView.findViewById(R.id.group); textView.setText(firstVisibleGroup.getTitle()); } @Override public boolean giveUpTouchEvent(MotionEvent event) { if (expandableListView.getFirstVisiblePosition() == 0) { View view = expandableListView.getChildAt(0); if (view != null && view.getTop() >= 0) { return true; } } return false; } }
public class StickyLayout extends LinearLayout { private static final String TAG = "StickyLayout"; private static final boolean DEBUG = true; public interface OnGiveUpTouchEventListener { public boolean giveUpTouchEvent(MotionEvent event); } private View mHeader; private View mContent; private OnGiveUpTouchEventListener mGiveUpTouchEventListener; // header的高度 单位:px private int mOriginalHeaderHeight; private int mHeaderHeight; public static final int STATUS_EXPANDED = 1; public static final int STATUS_COLLAPSED = 2; private int mStatus = STATUS_EXPANDED; private int mTouchSlop; // 分别记录上次滑动的坐标 private int mLastX = 0; private int mLastY = 0; // 分别记录上次滑动的坐标(onInterceptTouchEvent) private int mLastXIntercept = 0; private int mLastYIntercept = 0; // 用来控制滑动角度,仅当角度a满足如下条件才进行滑动:tan a = deltaX / deltaY > 2 private static final int TAN = 2; private boolean mIsSticky = true; private boolean mInitDataSucceed = false; private boolean mDisallowInterceptTouchEventOnHeader = true; public StickyLayout(Context context) { super(context); } public StickyLayout(Context context, AttributeSet attrs) { super(context, attrs); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public StickyLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); if (hasWindowFocus && (mHeader == null || mContent == null)) { initData(); } } private void initData() { // 这里获取view的方式和以前不一样,一般是使用getChildAt(index) // 获取应用包下的指定资源ID int headerId = getResources().getIdentifier("sticky_header", "id", getContext().getPackageName()); int contentId = getResources().getIdentifier("sticky_content", "id", getContext().getPackageName()); if (headerId != 0 && contentId != 0) { mHeader = findViewById(headerId); mContent = findViewById(contentId); mOriginalHeaderHeight = mHeader.getMeasuredHeight(); mHeaderHeight = mOriginalHeaderHeight; mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); if (mHeaderHeight > 0) { mInitDataSucceed = true; } if (DEBUG) { Log.d(TAG, "mTouchSlop = " + mTouchSlop + "mHeaderHeight = " + mHeaderHeight); } } else { throw new NoSuchElementException("Did your view with id \"sticky_header\" or \"sticky_content\" exists?"); } } public void setOnGiveUpTouchEventListener(OnGiveUpTouchEventListener l) { mGiveUpTouchEventListener = l; } /** * * 在头部布局HeaderView出现和可展开列表展开时,用户的上下滑动都是对HeaderView的操作 -> 事件被父容器StickyLayout拦截处理了 * 在头部布局HeaderView出现和可展开列表展开时,用户点击PinnedHeaderExpandableListView的组布局和item时都是PinnedHeaderExpandableListView自己处理的 * 在头部布局HeaderView隐藏时,事件都交由PinnedHeaderExpandableListView处理 * 用户在HeaderView上滑动时,onInterceptTouchEvent()就执行了一次,就一直执行onTouchEvent() * */ @Override public boolean onInterceptTouchEvent(MotionEvent event) { int intercepted = 0; int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { mLastXIntercept = x; mLastYIntercept = y; mLastX = x; mLastY = y; intercepted = 0; break; } case MotionEvent.ACTION_MOVE: { int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; // 如果滑动头像布局没执行该语句,滑动头布局时onInterceptTouchEvent返回false,DOWN逻辑没有执行, // 滑动非头部布局时不走DOWN逻辑,直接执行MOVE逻辑 Log.d("@@@", " y = " + y + " , getHeaderHeight = " + getHeaderHeight()); if (mDisallowInterceptTouchEventOnHeader && y <= getHeaderHeight()) { /** 该逻辑不会执行 */ intercepted = 0; Log.d("@@@", "y <= getHeaderHeight()"); } else if (Math.abs(deltaY) <= Math.abs(deltaX)) { intercepted = 0; } else if (mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop) { intercepted = 1; Log.d("@@@", "STATUS_EXPANDED"); } else if (mGiveUpTouchEventListener != null) { // 头部布局出现时当用户手指向上滑动的时候如果deltaY > -mTouchSlog // 头部布局未出现,用户手指滑动时 if (mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop) { intercepted = 1; Log.d("@@@", "giveUpTouchEvent"); } } break; } case MotionEvent.ACTION_UP: { intercepted = 0; mLastXIntercept = mLastYIntercept = 0; break; } default: break; } boolean result = intercepted != 0 && mIsSticky; Log.d("@@@", "onInterceptTouchEvent->result = " + result); return result; } @Override public boolean onTouchEvent(MotionEvent event) { Log.d("@@@", "onTouchEvent"); if (!mIsSticky) { return true; } int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { break; } case MotionEvent.ACTION_MOVE: { int deltaY = y - mLastY; // if (DEBUG) { // Log.d(TAG, "mHeaderHeight=" + mHeaderHeight + " deltaY=" + deltaY + " mlastY=" + mLastY); // } mHeaderHeight += deltaY; setHeaderHeight(mHeaderHeight); break; } case MotionEvent.ACTION_UP: { // 这里做了下判断,当松开手的时候,会自动向两边滑动,具体向哪边滑,要看当前所处的位置 int destHeight = 0; if (mHeaderHeight <= mOriginalHeaderHeight * 0.5) { destHeight = 0; mStatus = STATUS_COLLAPSED; } else { destHeight = mOriginalHeaderHeight; mStatus = STATUS_EXPANDED; } // 慢慢滑向终点 this.smoothSetHeaderHeight(mHeaderHeight, destHeight, 500); break; } default: break; } mLastX = x; mLastY = y; return true; } public void smoothSetHeaderHeight(final int from, final int to, long duration) { smoothSetHeaderHeight(from, to, duration, false); } public void smoothSetHeaderHeight(final int from, final int to, long duration, final boolean modifyOriginalHeaderHeight) { final int frameCount = (int) (duration / 1000f * 30) + 1; final float step = (to - from) / (float) frameCount; new Thread("Thread#smoothSetHeaderHeight") { @Override public void run() { for (int i = 0; i < frameCount; i++) { final int height; if (i == frameCount - 1) { height = to; } else { height = (int) (from + step * i); } post(new Runnable() { public void run() { setHeaderHeight(height); } }); try { sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } if (modifyOriginalHeaderHeight) { setOriginalHeaderHeight(to); } } }.start(); } public void setOriginalHeaderHeight(int originalHeaderHeight) { mOriginalHeaderHeight = originalHeaderHeight; } public void setHeaderHeight(int height, boolean modifyOriginalHeaderHeight) { if (modifyOriginalHeaderHeight) { setOriginalHeaderHeight(height); } setHeaderHeight(height); } public void setHeaderHeight(int height) { if (!mInitDataSucceed) { initData(); } if (height <= 0) { height = 0; } else if (height > mOriginalHeaderHeight) { height = mOriginalHeaderHeight; } if (height == 0) { mStatus = STATUS_COLLAPSED; } else { mStatus = STATUS_EXPANDED; } if (mHeader != null && mHeader.getLayoutParams() != null) { mHeader.getLayoutParams().height = height; mHeader.requestLayout(); mHeaderHeight = height; } else { if (DEBUG) { Log.e(TAG, "null LayoutParams when setHeaderHeight"); } } } public int getHeaderHeight() { return mHeaderHeight; } public void setSticky(boolean isSticky) { mIsSticky = isSticky; } public void requestDisallowInterceptTouchEventOnHeader(boolean disallowIntercept) { mDisallowInterceptTouchEventOnHeader = disallowIntercept; } }
public class MainActivity extends Activity implements ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupClickListener, OnHeaderUpdateListener, OnGiveUpTouchEventListener { private PinnedHeaderExpandableListView expandableListView; private StickyLayout stickyLayout; private ArrayList<Group> groupList; private ArrayList<List<People>> childList; private MyexpandableListAdapter adapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); expandableListView = (PinnedHeaderExpandableListView) findViewById(R.id.expandablelist); stickyLayout = (StickyLayout)findViewById(R.id.sticky_layout); initData(); adapter = new MyexpandableListAdapter(this); expandableListView.setAdapter(adapter); // 展开所有group for (int i = 0, count = expandableListView.getCount(); i < count; i++) { expandableListView.expandGroup(i); } expandableListView.setOnHeaderUpdateListener(this); expandableListView.setOnChildClickListener(this); expandableListView.setOnGroupClickListener(this); stickyLayout.setOnGiveUpTouchEventListener(this); } /*** * InitData */ void initData() { groupList = new ArrayList<Group>(); Group group = null; for (int i = 0; i < 3; i++) { group = new Group(); group.setTitle("group-" + i); groupList.add(group); } childList = new ArrayList<List<People>>(); for (int i = 0; i < groupList.size(); i++) { ArrayList<People> childTemp; if (i == 0) { childTemp = new ArrayList<People>(); for (int j = 0; j < 13; j++) { People people = new People(); people.setName("yy-" + j); people.setAge(30); people.setAddress("sh-" + j); childTemp.add(people); } } else if (i == 1) { childTemp = new ArrayList<People>(); for (int j = 0; j < 8; j++) { People people = new People(); people.setName("ff-" + j); people.setAge(40); people.setAddress("sh-" + j); childTemp.add(people); } } else { childTemp = new ArrayList<People>(); for (int j = 0; j < 23; j++) { People people = new People(); people.setName("hh-" + j); people.setAge(20); people.setAddress("sh-" + j); childTemp.add(people); } } childList.add(childTemp); } } /*** * 数据源 * * @author Administrator * */ class MyexpandableListAdapter extends BaseExpandableListAdapter { private Context context; private LayoutInflater inflater; public MyexpandableListAdapter(Context context) { this.context = context; inflater = LayoutInflater.from(context); } // 返回父列表个数 @Override public int getGroupCount() { return groupList.size(); } // 返回子列表个数 @Override public int getChildrenCount(int groupPosition) { return childList.get(groupPosition).size(); } @Override public Object getGroup(int groupPosition) { return groupList.get(groupPosition); } @Override public Object getChild(int groupPosition, int childPosition) { return childList.get(groupPosition).get(childPosition); } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public boolean hasStableIds() { return true; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { GroupHolder groupHolder = null; if (convertView == null) { groupHolder = new GroupHolder(); convertView = inflater.inflate(R.layout.group, null); groupHolder.textView = (TextView) convertView .findViewById(R.id.group); groupHolder.imageView = (ImageView) convertView .findViewById(R.id.image); convertView.setTag(groupHolder); } else { groupHolder = (GroupHolder) convertView.getTag(); } groupHolder.textView.setText(((Group) getGroup(groupPosition)) .getTitle()); if (isExpanded)// ture is Expanded or false is not isExpanded groupHolder.imageView.setImageResource(R.drawable.expanded); else groupHolder.imageView.setImageResource(R.drawable.collapse); return convertView; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { ChildHolder childHolder = null; if (convertView == null) { childHolder = new ChildHolder(); convertView = inflater.inflate(R.layout.child, null); childHolder.textName = (TextView) convertView .findViewById(R.id.name); childHolder.textAge = (TextView) convertView .findViewById(R.id.age); childHolder.textAddress = (TextView) convertView .findViewById(R.id.address); childHolder.imageView = (ImageView) convertView .findViewById(R.id.image); Button button = (Button) convertView .findViewById(R.id.button1); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "clicked pos=", Toast.LENGTH_SHORT).show(); } }); convertView.setTag(childHolder); } else { childHolder = (ChildHolder) convertView.getTag(); } childHolder.textName.setText(((People) getChild(groupPosition, childPosition)).getName()); childHolder.textAge.setText(String.valueOf(((People) getChild( groupPosition, childPosition)).getAge())); childHolder.textAddress.setText(((People) getChild(groupPosition, childPosition)).getAddress()); return convertView; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } } @Override public boolean onGroupClick(final ExpandableListView parent, final View v, int groupPosition, final long id) { return false; } @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { Toast.makeText(MainActivity.this, childList.get(groupPosition).get(childPosition).getName(), 1) .show(); return false; } class GroupHolder { TextView textView; ImageView imageView; } class ChildHolder { TextView textName; TextView textAge; TextView textAddress; ImageView imageView; } @Override public View getPinnedHeader() { View headerView = (ViewGroup) getLayoutInflater().inflate(R.layout.group, null); headerView.setLayoutParams(new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); return headerView; } @Override public void updatePinnedHeader(View headerView, int firstVisibleGroupPos) { Group firstVisibleGroup = (Group) adapter.getGroup(firstVisibleGroupPos); TextView textView = (TextView) headerView.findViewById(R.id.group); textView.setText(firstVisibleGroup.getTitle()); } @Override public boolean giveUpTouchEvent(MotionEvent event) { if (expandableListView.getFirstVisiblePosition() == 0) { View view = expandableListView.getChildAt(0); if (view != null && view.getTop() >= 0) { return true; } } return false; } }