Android 基于Activity的悬浮球实现,无需申请悬浮框权限、适合展示在游戏主页之上

  • 一般在接入渠道SDK的时候,他们的SDK里面都会带有悬浮球,并且浮动显示在您的应用之上的,当点击悬浮球就能弹出菜单栏或者游戏中心、用户中心之类的页面,有些SDK的实现是需要申请权限的,如果用户不给权限,那么悬浮球功能将无法使用了,这种方式肯定是不行的,下面我们就来实现一种依附在Activity页面上的悬浮球。
  • 首先我们需要自定义一个View,可以继承ImageView、或者其他View,然后重新onMeasure、onDraw、onTouchEvent等方法来实现拖动的逻辑
    public class DragView extends ImageView {
        private float downX, downY;
        private int width, height;
        private final int screenWidth, screenHeight;
        private int l, t, r, b;
        boolean isDoLayout = false;
        private boolean isDrag = false;
    
        private OnClickCallback onClickCallback;
    
        public interface OnClickCallback {
            void onClick(View v);
        }
    
        public DragView(Context context, AttributeSet attrs) {
            super(context, attrs);
            screenWidth = context.getResources().getDisplayMetrics().widthPixels;
            screenHeight = context.getResources().getDisplayMetrics().heightPixels - getStatusBarHeight();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            width = getMeasuredWidth();
            height = getMeasuredHeight();
        }
    
        public int getStatusBarHeight() {
            int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
            return getResources().getDimensionPixelSize(resourceId);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (isDoLayout) {
                doLayout(l, t, r, b);
            }
        }
    
        public void doLayout(int l, int t, int r, int b) {
            isDoLayout = true;
            this.l = l;
            this.t = t;
            this.r = r;
            this.b = b;
            this.layout(l, t, r, b);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
            if (this.isEnabled()) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        isDrag = false;
                        downX = event.getX();
                        downY = event.getY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        final float xDistance = event.getX() - downX;
                        final float yDistance = event.getY() - downY;
                        int l, r, t, b;
                        if (Math.abs(xDistance) > 10 || Math.abs(yDistance) > 10) {
                            isDrag = true;
                            l = (int) (getLeft() + xDistance);
                            r = l + width;
                            t = (int) (getTop() + yDistance);
                            b = t + height;
                            if (l < 0) {
                                l = 0;
                                r = l + width;
                            } else if (r > screenWidth) {
                                r = screenWidth;
                                l = r - width;
                            }
                            if (t < 0) {
                                t = 0;
                                b = t + height;
                            } else if (b > screenHeight) {
                                b = screenHeight;
                                t = b - height;
                            }
    
                            doLayout(l, t, r, b);
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        if (getLeft() + getWidth() / 2 <= screenWidth / 2) {
                            doLayout(0, getTop(), getWidth(), getBottom());
                        } else {
                            doLayout(screenWidth - getWidth(), getTop(), screenWidth, getBottom());
                        }
                        setPressed(false);
                        if (!isDrag) {
                            if (onClickCallback != null) {
                                onClickCallback.onClick(this);
                            }
                        }
                    case MotionEvent.ACTION_CANCEL:
                        setPressed(false);
                        break;
                }
                return true;
            }
            return false;
        }
    
        public OnClickCallback getOnClickCallback() {
            return onClickCallback;
        }
    
        public void setOnClickCallback(OnClickCallback onClickCallback) {
            this.onClickCallback = onClickCallback;
        }
    }
  • 实现好了自定义DragView后,还需要将DragView放入一个透明背景的布局,这个布局的宽和高都要充满父布局,背景需要设置为透明背景,为什么需要这样做呢,原因是如果我们的DragView显示在游戏上面的话,进行拖拽DragView会消失,并且留下一个框框,加个透明背景的父布局就能解决这个问题了,如下是布局文件
    <?xml version="1.0" encoding="utf-8"?>
    
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/content_wrap"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent">
    
        <com.lcq.floatview.DragView
            android:id="@+id/floating_menu_div"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_gravity="center_vertical"
            android:scaleType="fitXY"
            android:src="@drawable/qiuqiu"
            android:visibility="visible" />
    </FrameLayout>
  • 然后我们再写一个FloatMenu类,将这个布局文件进行加载,并提供show()、dismiss()接口以及构造函数,如下
    public class FloatMenu {
        private final DragView menuIv;
        private final FrameLayout floatView;
        private final FrameLayout.LayoutParams layoutParams;
        private final Activity activity;
        private boolean addedContentView = false;
    
        public FloatMenu(final Activity activity) {
            this.activity = activity;
            floatView = (FrameLayout) LayoutInflater.from(activity).inflate(R.layout.floating_menu, null);
            menuIv = floatView.findViewById(R.id.floating_menu_div);
            menuIv.setOnClickCallback(new DragView.OnClickCallback() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(activity, "您点击了球球", Toast.LENGTH_LONG).show();
                }
            });
            layoutParams = new FrameLayout.LayoutParams(activity.getResources().getDisplayMetrics().widthPixels,
                    activity.getResources().getDisplayMetrics().heightPixels);
            layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
        }
    
        public void show() {
            if (!addedContentView) {
                activity.getWindow().addContentView(floatView, layoutParams);
                addedContentView = true;
            }
            floatView.setVisibility(View.VISIBLE);
        }
    
        public void dismiss() {
            floatView.setVisibility(View.INVISIBLE);
        }
    }
  • 然后在需要使用的Activity界面,构建一个FloatMenu,在Activity的onResume()、onPause() 生命周期里分别调用FloatMenu的show()、dismiss()接口,比如
    public class MainActivity extends Activity {
        FloatMenu floatMenu;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            floatMenu = new FloatMenu(this);
        }
    
        @Override
        protected void onResume() {
            floatMenu.show();
            super.onResume();
        }
    
        @Override
        protected void onPause() {
            floatMenu.dismiss();
            super.onPause();
        }
    }
  • 你也可以手动进行调用show()和dismiss()方法展示或关闭悬浮球,界面显示效果如下
  • 如果是游戏应用的话,那么这种方式就很实用了,不需要申请权限、只需要在游戏主界面的生命周期里面调用相关的方法就可以了,如果是有很多界面需要展示悬浮球的话,可以对Activity栈里的每个Activity的生命周期进行监听,并创建、显示、关闭、销毁FloatMenu。具体的实现方式这里就不讲述了,大家可以去试试。
  • 如果需要源码的可以访问这个地址:https://gitee.com/lin-ciqiao/float-view

猜你喜欢

转载自blog.csdn.net/qq_19942717/article/details/124513846