Android仿QQ聯絡人分組懸停
阿新 • • 發佈:2019-02-02
看效果:
配合pullrefresh還能做到下拉重新整理,已實現(親測)。
直接上原始碼:
使用注意事項:/** * Created by carson on 2017/11/14. * 實現了分組懸停group header * @since 1.1.4 */ public class PinnedExHeaderListview extends ExpandableListView implements AbsListView.OnScrollListener { private HeaderListener mHeadListener; /** * 用於在列表頭顯示的 View,mHeaderViewVisible 為 true 才可見 */ private View mHeaderView; /** * 列表頭是否可見 */ private boolean mHeaderViewVisible; private int mHeaderViewWidth; private int mHeaderViewHeight; public PinnedExHeaderListview(Context context) { super(context); } public interface HeaderListener { public static final int PINNED_HEADER_GONE = 0; public static final int PINNED_HEADER_VISIBLE = 1; public static final int PINNED_HEADER_PUSHED_UP = 2; /** * 獲取 Header 的狀態 * * @param groupPosition * @param childPosition * @return PINNED_HEADER_GONE, PINNED_HEADER_VISIBLE, PINNED_HEADER_PUSHED_UP * 其中之一 */ int getHeaderState(int groupPosition, int childPosition); /** * @param header * @param groupPosition * @param childPosition */ void updateHeader(View header, int groupPosition, int childPosition); } public void initHeaderView(View view) { mHeaderView = view; if (mHeaderView != null) { AbsListView.LayoutParams lp = new AbsListView.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); view.setLayoutParams(lp); setFadingEdgeLength(0); } } public void setHeaderViewListener(HeaderListener listener) { this.mHeadListener = listener; setOnScrollListener(this); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mHeaderView != null) { long flatPos = getExpandableListPosition(firstVisibleItem); int groupPosition = ExpandableListView.getPackedPositionGroup(flatPos); int childPosition = ExpandableListView.getPackedPositionChild(flatPos); configureHeaderView(groupPosition, childPosition); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mHeaderView!=null){ int x = (int) ev.getX(); int y = (int) ev.getY(); int pos = pointToPosition(x, y); if (y >= mHeaderView.getTop() && y <= mHeaderView.getBottom()) { if (ev.getAction() == MotionEvent.ACTION_UP) { int groupPosition = getPackedPositionGroup(getExpandableListPosition(pos)); System.out.println("this is :"+ groupPosition +getExpandableListAdapter().getGroup(groupPosition).toString()); if (groupPosition != INVALID_POSITION) { int point = mHeaderView.getWidth()-x; if(point< Utils.convertDpToPixel(70)&&point>Utils.convertDpToPixel(15)){ ToastUtil.showMessage("icon click"+groupPosition); } // if (isGroupExpanded(groupPosition)) { // collapseGroup(groupPosition); // } else { // expandGroup(groupPosition); // } } } return true; } } return super.dispatchTouchEvent(ev); } private float mDownX; private float mDownY; /** * 如果 HeaderView 是可見的 , 此函式用於判斷是否點選了 HeaderView, 並對做相應的處理 , 因為 HeaderView * 是畫上去的 , 所以設定事件監聽是無效的 , 只有自行控制 . */ // @Override // public boolean onTouchEvent(MotionEvent ev) { // if (mHeaderViewVisible) { // switch (ev.getAction()) { // case MotionEvent.ACTION_DOWN: // mDownX = ev.getX(); // mDownY = ev.getY(); // if (mDownX <= mHeaderViewWidth && mDownY <= mHeaderViewHeight) { // return true; // } // break; // case MotionEvent.ACTION_UP: // float x = ev.getX(); // float y = ev.getY(); // float offsetX = Math.abs(x - mDownX); // float offsetY = Math.abs(y - mDownY); // // 如果 HeaderView 是可見的 , 點選在 HeaderView 內 , 那麼觸發 headerClick() // if (x <= mHeaderViewWidth && y <= mHeaderViewHeight // && offsetX <= mHeaderViewWidth // && offsetY <= mHeaderViewHeight) { // if (mHeaderView != null) { // headerViewClick(); // } // return true; // } // break; // default: // break; // } // } // return super.onTouchEvent(ev); // } /** * 點選 HeaderView 觸發的事件 */ private void headerViewClick() { long packedPosition = getExpandableListPosition(this .getFirstVisiblePosition()); int groupPosition = ExpandableListView .getPackedPositionGroup(packedPosition); this.collapseGroup(groupPosition); this.setSelectedGroup(groupPosition); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mHeaderView != null) { measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec); mHeaderViewWidth = mHeaderView.getMeasuredWidth(); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); } } private int mOldState = -1; @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (this.mHeadListener != null) { long flatPostion = getExpandableListPosition(getFirstVisiblePosition()); int groupPos = ExpandableListView .getPackedPositionGroup(flatPostion); int childPos = ExpandableListView .getPackedPositionChild(flatPostion); int state = this.mHeadListener.getHeaderState(groupPos, childPos); if (mHeaderView != null && state != mOldState) { mOldState = state; mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight); } configureHeaderView(groupPos, childPos); } } public void configureHeaderView(int groupPosition, int childPosition) { if (groupPosition == -1 || mHeaderView == null || this.mHeadListener == null || getExpandableListAdapter().getGroupCount() == 0) { return; } int state = mHeadListener.getHeaderState(groupPosition, childPosition); switch (state) { case HeaderListener.PINNED_HEADER_GONE: { mHeaderViewVisible = false; break; } case HeaderListener.PINNED_HEADER_VISIBLE: { mHeadListener.updateHeader(mHeaderView, groupPosition, childPosition); if (mHeaderView.getTop() != 0) { mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight); } mHeaderViewVisible = true; break; } case HeaderListener.PINNED_HEADER_PUSHED_UP: { View firstView = getChildAt(0); int bottom = firstView.getBottom(); // intitemHeight = firstView.getHeight(); int headerHeight = mHeaderView.getHeight(); int y; if (bottom < headerHeight) { y = (bottom - headerHeight); // alpha = MAX_ALPHA * (headerHeight + y) / headerHeight; } else { y = 0; } mHeadListener.updateHeader(mHeaderView, groupPosition, childPosition); if (mHeaderView.getTop() != y) { mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y); } mHeaderViewVisible = true; break; } } } @Override /** * 列表介面更新時呼叫該方法(如滾動時) */ protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mHeaderViewVisible && mHeaderView != null) { // 分組欄是直接繪製到介面中,而不是加入到ViewGroup中 drawChild(canvas, mHeaderView, getDrawingTime()); } } }
R.layout.item_group_root為分組的view,就是懸停時的view
//support group header floatting pinnedListview.initHeaderView(getLayoutInflater().inflate(R.layout.item_group_root, groupExpandablelist, false)); pinnedListview.setHeaderViewListener(new PinnedExHeaderListview.HeaderListener() { @Override public int getHeaderState(int groupPosition, int childPosition) { if (groupPosition == -1) { return PINNED_HEADER_GONE; } int childCount = adapter.getChildrenCount(groupPosition); if (childPosition == childCount - 1) { return PINNED_HEADER_PUSHED_UP; } else if (childPosition == -1 && !pinnedListview.isGroupExpanded(groupPosition)) { return PINNED_HEADER_GONE; } else { return PINNED_HEADER_VISIBLE; } } @Override public void updateHeader(View header, int groupPosition, int childPosition) { if (groupPosition > -1) { TextView group = (TextView) header.findViewById(R.id.root_name); if (group != null) { group.setText(adapter.getGroup(groupPosition).getModule_name()); } } } });