PopupMenu弹出位置的控制

PopupMenu作为弹出菜单是很好用的,但是默认只能弹出在view的下方,而实际中这样的弹出位置可能无法满足需求,比如自定义的canvasView,要在canvasView长按的位置弹出菜单,PopupMenu只有一个show的方法,没有可以设置位置的方法,但当我们跟进源码去看时发现了这样的一段代码:

PopupMenu.class

public void show() {
        this.mPopup.show();
    }

再对mPopup.show跟踪时进入到了MenuPopupHelper,又有如下的代码

  public void show() {
        if (!this.tryShow()) {
            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
        }
    }

    public void show(int x, int y) {
        if (!this.tryShow(x, y)) {
            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
        }
    }

    public boolean tryShow() {
        if (this.isShowing()) {
            return true;
        } else if (this.mAnchorView == null) {
            return false;
        } else {
            this.showPopup(0, 0, false, false);
            return true;
        }
    }

this.mPopup.show调用的是tryShow,而tryShow又调用的是showPopup,在showPopup的参数中有传入xOffset和yOffset,这说明里面是有传偏移量的,再仔细看MenuPopupHelper的show函数发现有show(x,y)的重载,如果我们能调用show(x,y),可能就能满足需求。

但MenuPopupHelper又没办法直接得到,是包装到PopupMenu中的,于是我们采用反射的方式来获取,代码如下

  Field field = popupMenu.getClass().getDeclaredField("mPopup");
            field.setAccessible(true);
            MenuPopupHelper helper = (MenuPopupHelper) field.get(popupMenu);
            helper.show(x, y);

通过反射是得到了MenuPopupHelper,但是会提示错误MenuPopupHelper.show can only be called from within the same library group (groupId=com.android.support),跟入到MenuPopupHelper类里面,可以看到有如下图的限定

PopupMenu弹出位置的控制_第1张图片

为此,我们需要将刚刚反射的部分特别的封装到一个方法中,并在方法上加入@SuppressLint("RestrictedApi"),这样就可以正常运行了,代码如下

public class MenuWorker implements PopupMenu.OnMenuItemClickListener
{
    @SuppressLint("RestrictedApi")
    private void showPopupMenu(int x, int y) {
        if (!_drawActivity.isEditing()) {
            return;
        }
        //创建弹出式菜单对象(最低版本11)
        PopupMenu popupMenu = new PopupMenu(_drawActivity, _view);//第二个参数是绑定的那个view,菜单弹出时默认是显示该view下方的。
        initMenu(popupMenu.getMenu());
        //绑定菜单项的点击事件
        popupMenu.setOnMenuItemClickListener(this);
        //显示
        //popupMenu.show();//默认显示在view的下方,如果要控制具体显示位置,需要使用反射来实现。

        try {
            Field field = popupMenu.getClass().getDeclaredField("mPopup");
            field.setAccessible(true);
            MenuPopupHelper helper = (MenuPopupHelper) field.get(popupMenu);
            y = y - _view.getHeight();//如果y取的是触摸点的位置,可能需要作此处理,经测试android5.1的设备会弹窗在屏幕之外
            helper.show(x, y);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    /**
     * 初始化菜单
     *
     * @param menu
     */
    public void initMenu(Menu menu) {
        menu.clear();
        
        menu.add(1, 10001, 0, "添加");
        menu.add(1, 10002, 1, "删除");
        menu.add(1, 10003, 2, "切换");
        
    }

 /**
     * 菜单点击
     *
     * @param menuItem
     * @return
     */
    @Override
    public boolean onMenuItemClick(MenuItem menuItem) {
        switch (menuItem.getItemId()) {
            case 10001: {
                //添加
                break;
            }
            case 10002: {
                //删除
                break;
            }
            case 10003: {
                //切换;
                break;
            }
            default:
                break;
        }
        return false;
    }
}

PopupMenu弹出位置的控制_第2张图片

转载请注明出处

你可能感兴趣的:(Android)