仿SegmentFault系列(一) PopupMenu

仿SegmentFault系列(一) PopupMenu

前言

一直觉得SegmentFault安卓客户端做的很不错,特别棒的MetrialDesign风格实践,当然包括知乎我也很喜欢,不过有一天我不小心反编出了SegmentFault的api接口,所以我决定亲自实现一遍SegmentFault安卓客户端。希望能通过这一系列的博客将其中的技术要点记录下来,跟大家一起分享。网络上很容易找到的资料这里就不再赘述,重点是分大家分享一下瞎研究的奇技淫巧和黑科技。
这次选择了PopupMenu作为第一篇,多少还是因为太复杂的东西怕自己hold不住,水平有限,希望能给大家带来一个清晰的开篇。

先走一波效果图

这里写图片描述
基本上是还原了SegmentFault中分享菜单的设计,只是SegmentFault种菜单的selector并没有使用ripple效果,不过抛开这个,其他地方可以说是一模一样了。

技术选型

如果说到分享的话,最直觉的做法就是使用ShareActionProvider了,于是网上找了一波资料后发现做出来是这样的??
这里写图片描述
这个问题不小啊… 首先菜单位置并不是顶到最上面的啊??而且我这个分享图标怎么蜜汁变大了呢??而且我怎么控制菜单里显示的东西呢??人家上来显示微博分享、微信分享、各种分享的,我这怎么成了小米快传了??
然后我又点开了查看全部
这里写图片描述
这又是什么情况?? 是我身体不行了看东西有重影了??看起来像是新的菜单没有完全遮挡住旧的菜单,然后出现重叠了
接着我不小心手抖点了下ForkHub,等我退回来的时候就酱了。。
这里写图片描述
对没错我的toolbar上乱入了一直喵咪,应该是谷歌贴心的认为这是你最常用的分享应用,所以就给你显示在这里了,可是我一句话都没说好吗?!
心灰意冷,所以最终决定使用popupMenu来实现动图中所出现的效果。所以只要我们能够拿到系统中支持分享的应用列表,让popupMenu显示出来就可以了。

好了我要开始分析源码了!

为了实现最终效果,还真得好好看一遍PopupMenu的实现方式,我就假设大家都对PopupMenu的使用有一点了解,所以会更多地偏向原理而不讨论使用方法。在讲PopupMenu之前,还是得从ListPopupWindow说起

ListPopupWindow

ListPopupWindow是supportv7包提供的控件,以popup的方式展示一个list,所以说,ListPopupWindow本质上维护了一个PopupWindow并将其内容设置为一个ListView,我们可以看一下他的show()方法的代码

/**
     * Show the popup list. If the list is already showing, this method
     * will recalculate the popup's size and position.
     */
    public void show() {
        //在这里动态创建了popupwindow要展示的ListView
        int height = buildDropDown();
        ...
        //如果正在显示
        if (mPopup.isShowing()) {
            ...
            ...
            //重新算了一波宽高位置并更新了下
            mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
                            mDropDownVerticalOffset, (widthSpec < 0)? -1 : widthSpec,
                            (heightSpec < 0)? -1 : heightSpec);
        }else {
            //开始算宽度
            final int widthSpec;
            //如果是设置的宽度是MATCH_PARENT就按MATCH_PARENT来
            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
                widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
            } else {
                //如果设置的是WRAP_CONTENT就按AnchorView的宽度来
                if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
                    widthSpec = getAnchorView().getWidth();
                } else {
                    //或者是传进去一个确定的宽度就听你的
                    widthSpec = mDropDownWidth;
                }
            }
            //算了下高度  
            final int heightSpec;
            ...    
            mPopup.setWidth(widthSpec);
            mPopup.setHeight(heightSpec);
            ...
            //调用了显示PopupWindow的代码
            PopupWindowCompat.showAsDropDown(mPopup, getAnchorView(), mDropDownHorizontalOffset,
                    mDropDownVerticalOffset, mDropDownGravity);
            ...
    }

删掉了很多细节的代码,保留了函数大概的结构,在函数的开始就创建了一个listview并作为PopupWindow的content,如果你对这个buildDropDown()不是很放心,那我们就进来看一下

    private int buildDropDown() {
        ViewGroup dropDownView;
        int otherHeights = 0;

        if (mDropDownList == null) {
            Context context = mContext;
            ...
            ...
            //这个DropDownListView是一个ListView的子类
            //从这里开始创建并初始化它
            mDropDownList = new DropDownListView(context, !mModal);
            if (mDropDownListHighlight != null) {
                mDropDownList.setSelector(mDropDownListHighlight);
            }
            //这里的这个adpter是由使用者实例化并传入的
            mDropDownList.setAdapter(mAdapter);
            mDropDownList.setOnItemClickListener(mItemClickListener);
            mDropDownList.setFocusable(true);
            mDropDownList.setFocusableInTouchMode(true);
            mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                public void onItemSelected(AdapterView<?> parent, View view,
                        int position, long id) {

                    if (position != -1) {
                        DropDownListView dropDownList = mDropDownList;

                        if (dropDownList != null) {
                            dropDownList.mListSelectionHidden = false;
                        }
                    }
                }

                public void onNothingSelected(AdapterView<?> parent) {
                }
            });
            mDropDownList.setOnScrollListener(mScrollListener);

            if (mItemSelectedListener != null) {
                mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
            }

            dropDownView = mDropDownList;

            //他也支持在菜单的顶部或底部加一个用于提示的hintview
            //做法就是把hintView跟ListView放到一个LinearLayout里作为dropDownView
            View hintView = mPromptView;
            ...
            ...
            //把这个listview(准确说是dropDownView)放到popupWindow里了
            mPopup.setContentView(dropDownView);
        } else {
            dropDownView = (ViewGroup) mPopup.getContentView();
            ...
        }
        ...
        // Max height avail
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值