仿QQ的ListView:SpinnedHeaderExpandableListView实现

最近项目中遇到了可折叠ListView,但是要在滑动过程中有固定Title,方便用户可以点击全选或者收起展开的List的操作。


自定义ListView:

/**
 * @author fanlitao
 */
public class PinnedHeaderExpandableListView extends ExpandableListView implements OnScrollListener,
        OnGroupClickListener {

    public static final int GROUP_COLLAPSE_STATUS = 0;
    public static final int GROUP_EXPAND_STATUS = 1;

    public PinnedHeaderExpandableListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        registerListener();
    }

    public PinnedHeaderExpandableListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        registerListener();
    }

    public PinnedHeaderExpandableListView(Context context) {
        super(context);
        registerListener();
    }

    public interface PinnedSectionedHeaderAdapter {
        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;

        int getSectionHeaderState(int groupPosition, int childPosition);

        void configureSectionHeader(View header, int groupPosition, int childPosition, int alpha);

        void setGroupClickStatus(int groupPosition, int status);

        int getGroupClickStatus(int groupPosition);

        void onSectionHeaderCheckBoxClick(View header, int groupPosition);

    }

    private static final int MAX_ALPHA = 255;

    private PinnedSectionedHeaderAdapter mAdapter;

    private View mHeaderView;

    private boolean mHeaderViewVisible;

    private int mHeaderViewWidth;

    private int mHeaderViewHeight;

    private boolean mGroupEnable = true;

    public void setSelectionHeaderView(View view) {
        mHeaderView = view;
        AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        view.setLayoutParams(lp);

        if (mHeaderView != null) {
            setFadingEdgeLength(0);
        }

        requestLayout();
    }

    private void registerListener() {
        setOnScrollListener(this);
        setOnGroupClickListener(this);
    }

    private void headerViewClick() {
        long packedPosition = getExpandableListPosition(getFirstVisiblePosition());

        int groupPosition = ExpandableListView.getPackedPositionGroup(packedPosition);

        if (mAdapter.getGroupClickStatus(groupPosition) == GROUP_EXPAND_STATUS) {
            collapseGroup(groupPosition);
            mAdapter.setGroupClickStatus(groupPosition, GROUP_COLLAPSE_STATUS);
        }
        else {
            expandGroup(groupPosition);
            mAdapter.setGroupClickStatus(groupPosition, GROUP_EXPAND_STATUS);
        }

        setSelectedGroup(groupPosition);
    }

    private float mDownX;
    private float mDownY;

    @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();
                    int checkbox = OptimUtils.dip2px(getContext(), 100);
                    float offsetX = Math.abs(x - mDownX);
                    float offsetY = Math.abs(y - mDownY);
                    if ((x <= mHeaderViewWidth - checkbox) && y <= mHeaderViewHeight
                            && offsetX <= mHeaderViewWidth && offsetY <= mHeaderViewHeight) {
                        if (mHeaderView != null) {
                            headerViewClick();
                        }
                        return true;
                    }

                    if ((x > mHeaderViewWidth - checkbox) && y <= mHeaderViewHeight
                            && (offsetX < checkbox) && offsetY <= mHeaderViewHeight) {
                        if (mHeaderView != null) {
                            long packedPosition = getExpandableListPosition(this
                                    .getFirstVisiblePosition());
                            int groupPosition = ExpandableListView
                                    .getPackedPositionGroup(packedPosition);
                            mAdapter.onSectionHeaderCheckBoxClick(mHeaderView, groupPosition);
                        }
                        return true;
                    }
                    break;
                default:
                    break;
            }
        }

        return super.onTouchEvent(ev);

    }

    @Override
    public void setAdapter(ExpandableListAdapter adapter) {
        super.setAdapter(adapter);
        mAdapter = (PinnedSectionedHeaderAdapter) adapter;
    }

    @Override
    public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
        if (mGroupEnable) {
            if (mAdapter.getGroupClickStatus(groupPosition) == GROUP_COLLAPSE_STATUS) {
                mAdapter.setGroupClickStatus(groupPosition, GROUP_EXPAND_STATUS);
                parent.expandGroup(groupPosition);
                parent.setSelectedGroup(groupPosition);
            } else if (mAdapter.getGroupClickStatus(groupPosition) == GROUP_EXPAND_STATUS) {
                mAdapter.setGroupClickStatus(groupPosition, GROUP_COLLAPSE_STATUS);
                parent.collapseGroup(groupPosition);
            }
        }
        return true;
    }

    public void setGroupClickEnable(boolean state) {
        mGroupEnable = state;
    }

    @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);
        final long flatPostion = getExpandableListPosition(getFirstVisiblePosition());
        final int groupPos = ExpandableListView.getPackedPositionGroup(flatPostion);
        final int childPos = ExpandableListView.getPackedPositionChild(flatPostion);
        if (mAdapter != null) {
            int state = mAdapter.getSectionHeaderState(groupPos, childPos);
            if (mHeaderView != null && mAdapter != null && state != mOldState) {
                mOldState = state;
                mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
            }
        }

        configureHeaderView(groupPos, childPos);
    }

    public void configureHeaderView(int groupPosition, int childPosition) {
        if (mHeaderView == null || mAdapter == null
                || ((ExpandableListAdapter) mAdapter).getGroupCount() == 0) {
            return;
        }

        int state = mAdapter.getSectionHeaderState(groupPosition, childPosition);

        switch (state) {
            case PinnedSectionedHeaderAdapter.PINNED_HEADER_GONE: {
                mHeaderViewVisible = false;
                break;
            }

            case PinnedSectionedHeaderAdapter.PINNED_HEADER_VISIBLE: {
                mAdapter.configureSectionHeader(mHeaderView, groupPosition, childPosition,
                        MAX_ALPHA);

                if (mHeaderView.getTop() != 0) {
                    mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
                }
                mHeaderViewVisible = true;
                break;
            }

            case PinnedSectionedHeaderAdapter.PINNED_HEADER_PUSHED_UP: {
                View firstView = getChildAt(0);
                int bottom = firstView.getBottom();
                int headerHeight = mHeaderView.getHeight();
                int y;
                int alpha;

                if (bottom < headerHeight) {
                    y = (bottom - headerHeight);
                    alpha = MAX_ALPHA * (headerHeight + y) / headerHeight;
                } else {
                    y = 0;
                    alpha = MAX_ALPHA;
                }
                mAdapter.configureSectionHeader(mHeaderView, groupPosition, childPosition, alpha);

                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) {
            drawChild(canvas, mHeaderView, getDrawingTime());
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
            int totalItemCount) {
        final long flatPos = getExpandableListPosition(firstVisibleItem);
        int groupPosition = ExpandableListView.getPackedPositionGroup(flatPos);
        int childPosition = ExpandableListView.getPackedPositionChild(flatPos);
        configureHeaderView(groupPosition, childPosition);
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
    }
}


需要在这个ListView的Adapter中,实现SpinnedHeaderExpandableListView定义的接口。

以下是Adapter中的部分实现


    @Override
    public int getSectionHeaderState(int groupPosition, int childPosition) {
        final int childCount = getChildrenCount(groupPosition);
        if (childPosition == childCount - 1) {
            return PINNED_HEADER_PUSHED_UP;
        }
        else if (childPosition == -1 && !mListView.isGroupExpanded(groupPosition)) {
            return PINNED_HEADER_GONE;
        }
        else {
            return PINNED_HEADER_VISIBLE;
        }
    }


    @Override
    public void configureSectionHeader(View header, int groupPosition, int childPosition, int alpha) {
        TextView headerView = (TextView) header.findViewById(R.id.header_type);
        ImageView headerIcon = (ImageView) header.findViewById(R.id.junk_icon_header);
        GroupBean bean = (GroupBean) getGroup(groupPosition);
        headerView.setText(bean.name);
        headerIcon.setImageResource(bean.icon);
        final ImageView checkView = (ImageView) header.findViewById(R.id.checkbox);
        ImageView expandView = (ImageView) header.findViewById(R.id.expand_icon);
        expandView.setSelected(bean.isExpand);
        int size = mChilds.get(groupPosition).size();
        if (size == 0) {
            expandView.setVisibility(View.INVISIBLE);
            checkView.setVisibility(View.INVISIBLE);
        } else {
            checkView.setVisibility(View.VISIBLE);
            expandView.setVisibility(View.VISIBLE);
            SparseArray<AppBean> set = mSelectSet.get(groupPosition);
            if (mIsScanFinished) {
                checkView.setSelected(set.size() == size);
            } else {
                checkView.setSelected(mDefaultCheckState);
            }
        }
    }


    private HashMap<Integer, Integer> groupStatusMap = new HashMap<Integer, Integer>();


    @Override
    public void setGroupClickStatus(int groupPosition, int status) {
        groupStatusMap.put(groupPosition, status);
    }


    @Override
    public int getGroupClickStatus(int groupPosition) {
        if (groupStatusMap.containsKey(groupPosition)) {
            return groupStatusMap.get(groupPosition);
        }
        else {
            return 0;
        }
    }


这样我们就实现了仿QQ的ExpandableListView的效果。虽然还有一些问题,但是大体样子就该是这样。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值