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

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于 Activity 实现全局悬浮的代码示例: ```java public class FloatingActivity extends Activity implements View.OnTouchListener { private WindowManager mWindowManager; private WindowManager.LayoutParams mLayoutParams; private ImageView mFloatingView; private int mStartX, mStartY; private int mLastX, mLastY; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_floating); // 设置窗口属性 mLayoutParams = new WindowManager.LayoutParams(); mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; mLayoutParams.format = PixelFormat.RGBA_8888; mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mLayoutParams.gravity = Gravity.TOP | Gravity.START; // 添加悬浮 View mFloatingView = new ImageView(this); mFloatingView.setImageResource(R.drawable.ic_floating_ball); mFloatingView.setOnTouchListener(this); mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); mWindowManager.addView(mFloatingView, mLayoutParams); } @Override public boolean onTouch(View view, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 记录起始位置 mStartX = mLayoutParams.x; mStartY = mLayoutParams.y; // 记录上一次位置 mLastX = (int) event.getRawX(); mLastY = (int) event.getRawY(); break; case MotionEvent.ACTION_MOVE: // 计算移动距离 int dx = (int) (event.getRawX() - mLastX); int dy = (int) (event.getRawY() - mLastY); // 更新悬浮位置 mLayoutParams.x = mStartX + dx; mLayoutParams.y = mStartY + dy; mWindowManager.updateViewLayout(mFloatingView, mLayoutParams); break; case MotionEvent.ACTION_UP: // 处理悬浮点击事件 if (Math.abs(event.getRawX() - mLastX) < 10 && Math.abs(event.getRawY() - mLastY) < 10) { Intent intent = new Intent(this, FloatingBallClickActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); // 关闭悬浮窗口 mWindowManager.removeView(mFloatingView); finish(); } break; } return true; } } ``` 在上面的代码中,我们创建了一个继承自 Activity 的类 FloatingActivity,作为悬浮的容器。在 onCreate 方法中,我们设置了容器的布局、位置等属性,并添加了一个 ImageView 作为悬浮 View。在 onTouch 方法中,我们实现悬浮的拖动和点击事件,并使用 WindowManager 来更新悬浮的位置。在悬浮点击事件中,我们启动了一个新的 Activity 处理点击事件,并关闭了悬浮窗口。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值