手撸一个 微信长按菜单 - FloatMenu

2,080 阅读2分钟

在日常开发中,长按某个view出现个菜单是很常见的需求,Google 也给我们提供了一些组件来实现,比如PopupMenu。但是在实际使用中还是发现他无法满足我们的所有需求。

比如:产品要求长按菜单出现在手指按下的位置,额 这就头疼了,PopupMenu只能展示在view的底部或者头部。还有个问 题如果你的view过长超过一屏,那么 PopupMenu 就无法显示在屏幕里了。

所以没办法,只能硬着头皮自己撸一个menu了。 在日常使用中发现微信的 弹窗菜单 很符合要求,所以就模仿微信来撸一个了。

实现思路

  • 继承 PopupWindow 来实现长按弹窗

  • 获取当前按下 位置,传给 view 来显示

具体实现

  • 第一个很好实现,我用的是recyclerView 来实现布局的,而且传入的资源是 menu。而且另一个好处就是控制某些 item的可见(因为我自己项目中有这样需求,所以考虑进去了)
	private Menu mMenu;
	
	@NonNull
    @SuppressLint("RestrictedApi")
    public Menu getMenu() {
        if (mMenu == null) {
            mMenu = new MenuBuilder(mContext);
        }
        return mMenu;
    }

    @NonNull
    public MenuInflater getMenuInflater() {
        return new MenuInflater(mContext);
    }

    public void inflate(@MenuRes int menuRes) {
        getMenuInflater().inflate(menuRes, getMenu());
    }

然后就是填充数据到recyclerView中了


  • 第二个需求,就需要在调用的Activity 中重写dispatchTouchEvent获取当前点击位置,然后传给 menu 来判断显示位置了
private Point mPoint = new Point();

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            mPoint.x = (int) ev.getRawX();
            mPoint.y = (int) ev.getRawY();
        }
        return super.dispatchTouchEvent(ev);
    }
    public void showPopup(View anchorView, int x, int y) {
        if (!getMenu().hasVisibleItems()) {
            return;
        }

        //set visible item data
        int size = getMenu().size();
        mMenuItems.clear();
        for (int i = 0; i < size; i++) {
            MenuItem item = getMenu().getItem(i);
            if (item.isVisible()) {
                mMenuItems.add(item);
            }
        }
        mMenuAdapter.notifyDataSetChanged();
        //show
        int menuHeight = Display.dip2px(mContext, DEFAULT_ITEM_HEIGHT * mMenuItems.size());
        if (x <= mScreenPoint.x / 2) {
            if (y + menuHeight < mScreenPoint.y) {
                setAnimationStyle(R.style.Animation_top_left);
                showAtLocation(anchorView, ANCHORED_GRAVITY, x + X_OFFSET, y);
            } else {
                setAnimationStyle(R.style.Animation_bottom_left);
                showAtLocation(anchorView, ANCHORED_GRAVITY, x + X_OFFSET, y - menuHeight);
            }
        } else {
            if (y + menuHeight < mScreenPoint.y) {
                setAnimationStyle(R.style.Animation_top_right);
                showAtLocation(anchorView, ANCHORED_GRAVITY, x - mMenuWidth - X_OFFSET, y);
            } else {
                setAnimationStyle(R.style.Animation_bottom_right);
                showAtLocation(anchorView, ANCHORED_GRAVITY, x - mMenuWidth + X_OFFSET, y - menuHeight);
            }
        }

    }

效果如下

pic1.png

大致思路就是这样,最后奉上 源码,如果你觉得对你有用,欢迎点个赞