使用Android实现跨页面悬浮窗效果

 更新时间:2024年11月08日 10:09:00   作者:xiaoerbuyu1233  
这篇文章主要为大家详细介绍了如何使用Android实现跨页面悬浮窗效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

实现效果

在 Application#onCreate初始化

 FloatingWindowManager
                .getInstance(App.this).setImg(R.drawable.ic_test).setWidthHeight(280,280)
                .setOnFloatingClickListener(new FloatingWindowManager.OnFloatingClickListener() {
                    @Override
                    public void onClick(FrameLayout floatingView) {
                        Toast.makeText(floatingView.getContext(), "测试点击悬浮窗", Toast.LENGTH_SHORT).show();
                    }
                });

完整代码

import android.app.Activity;
import android.app.Application;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
 
import androidx.appcompat.app.AlertDialog;
 
import java.util.List;
 
/**
 * https://blog.csdn.net/xiaoerbuyu1233/article/details/143595475
 * <p>
 * 跨页面悬浮窗 Application#onCreate 初始化
 * <p>
 * FloatingWindowManager
 * .getInstance(App.this).setImg(R.drawable.ic_test).setWidthHeight(280,280)
 * .setOnFloatingClickListener(new FloatingWindowManager.OnFloatingClickListener() {
 *
 * @Override public void onClick(FrameLayout floatingView) {
 * Toast.makeText(floatingView.getContext(), "测试点击悬浮窗", Toast.LENGTH_SHORT).show();
 * }
 * });
 */
public class FloatingWindowManager {
    private static FloatingWindowManager instance;
    private final Context context;
    private FrameLayout floatingView;
    private ViewGroup rootView;
    private OnFloatingClickListener listener;
    private int width = 200, height = 200;
    private Application.ActivityLifecycleCallbacks activityLifecycleCallbacks;
 
    private FloatingWindowManager(Context context) {
        this.context = context;
        initFloatingView();
        registerActivityLifecycleCallbacks();
    }
 
    public static synchronized FloatingWindowManager getInstance(Context context) {
        if (instance == null) {
            instance = new FloatingWindowManager(context);
        }
        return instance;
    }
 
    private void registerActivityLifecycleCallbacks() {
        Application application = (Application) context.getApplicationContext();
        activityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                Log.d("FloatingWindowManager", "onActivityCreated: " + activity.getLocalClassName());
                // 可以在这里附加悬浮窗口到新创建的 Activity
                attachToActivity(activity);
            }
 
            @Override
            public void onActivityStarted(Activity activity) {
                Log.d("FloatingWindowManager", "onActivityStarted: " + activity.getLocalClassName());
            }
 
            @Override
            public void onActivityResumed(Activity activity) {
                Log.d("FloatingWindowManager", "onActivityResumed: " + activity.getLocalClassName());
                attachToActivity(activity);
            }
 
            @Override
            public void onActivityPaused(Activity activity) {
                detachFromActivity();
                Log.d("FloatingWindowManager", "onActivityPaused: " + activity.getLocalClassName());
            }
 
            @Override
            public void onActivityStopped(Activity activity) {
                Log.d("FloatingWindowManager", "onActivityStopped: " + activity.getLocalClassName());
            }
 
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
                Log.d("FloatingWindowManager", "onActivitySaveInstanceState: " + activity.getLocalClassName());
            }
 
            @Override
            public void onActivityDestroyed(Activity activity) {
                Log.d("FloatingWindowManager", "onActivityDestroyed: " + activity.getLocalClassName());
            }
        };
 
        // 注册 Activity 生命周期回调
        application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
    }
 
 
    public FloatingWindowManager setImg(int icon) {
        // 使用资源ID直接设置背景
        if (floatingView != null) {
            floatingView.setBackgroundResource(icon);
        }
//        // 获取Drawable对象
//        Drawable drawable = ContextCompat.getDrawable(context, R.drawable.ic_test);
//
//        // 设置背景
//        if (drawable != null) {
//            floatingView.setBackground(drawable);
//        }
        return this;
    }
 
    public FloatingWindowManager setWidthHeight(int width, int height) {
        this.width = width;
        this.height = height;
 
        // 设置布局参数
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height); // 宽度和高度
        layoutParams.gravity = Gravity.TOP | Gravity.START;
        floatingView.setLayoutParams(layoutParams);
        return this;
    }
 
    private void initFloatingView() {
        // 初始化悬浮窗视图
        floatingView = new FrameLayout(context) {
            @Override
            public boolean performClick() {
                if (super.performClick()) {
                    return true;
                }
                if (listener != null) {
                    floatingView.getHandler().post(() -> listener.onClick(floatingView));
                }
                return true;
            }
        };
 
        floatingView.setOnTouchListener(new View.OnTouchListener() {
            private int initialX, initialY;
            private float startX, startY;
            private static final int CLICK_DRAG_TOLERANCE = 40; // 点击和拖动之间的容差距离
 
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        initialX = (int) (event.getRawX() - v.getX());
                        initialY = (int) (event.getRawY() - v.getY());
                        startX = event.getRawX();
                        startY = event.getRawY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        int newX = (int) (event.getRawX() - initialX);
                        int newY = (int) (event.getRawY() - initialY);
                        if (rootView != null) {
                            v.setX(newX);
                            v.setY(newY);
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        float endX = event.getRawX();
                        float endY = event.getRawY();
                        float distanceX = Math.abs(endX - startX);
                        float distanceY = Math.abs(endY - startY);
                        if (distanceX < CLICK_DRAG_TOLERANCE && distanceY < CLICK_DRAG_TOLERANCE) {
                            // 如果移动的距离小于容差,那么我们认为这是一个点击
                            if (floatingView.performClick()) {
                                return true; // 点击已被处理
                            }
                        } else {
                            // 如果移动的距离大于容差,那么我们认为这是一个拖动
                            snapToEdge();
                        }
                        break;
                    default:
                        break;
                }
                return true; // 消耗事件
            }
        });
 
        // 设置布局参数
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height); // 宽度和高度
        layoutParams.gravity = Gravity.TOP | Gravity.START;
        floatingView.setLayoutParams(layoutParams);
    }
 
    public void attachToActivity(Activity activity) {
        if (activity == null || floatingView == null) return;
 
        rootView = (ViewGroup) activity.findViewById(android.R.id.content);
        if (rootView != null && floatingView.getParent() == null) {
            rootView.addView(floatingView);
        }
    }
 
    public void detachFromActivity() {
        if (rootView != null && floatingView != null && floatingView.getParent() != null) {
            rootView.removeView(floatingView);
        }
    }
 
    public void setOnFloatingClickListener(OnFloatingClickListener listener) {
        this.listener = listener;
    }
 
    private void snapToEdge() {
        // 实现贴边功能
        // 这里只是一个简单的例子,你可以根据自己的需求调整贴边的具体实现
        int[] location = new int[2];
        floatingView.getLocationOnScreen(location);
        int x = location[0];
        int y = location[1];
 
        int screenWidth = rootView.getWidth();
        int screenHeight = rootView.getHeight();
 
        int halfScreenWidth = screenWidth / 4; // 假设屏幕宽度的3/4处作为贴边判断点
        if (x < halfScreenWidth) {
            floatingView.setX(0);
        } else {
            floatingView.setX(screenWidth - floatingView.getWidth());
        }
    }
 
    public interface OnFloatingClickListener {
        void onClick(FrameLayout floatingView);
    }
 
    /**
     * 显示一个列表对话框,用户点击列表项后会将该项内容复制到剪贴板
     *
     * @param context 上下文
     * @param items   列表项数据
     */
    public static void showListDialogCopy(Context context, String title, List<String> items) {
        // 创建一个AlertDialog.Builder对象
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        // 设置对话框标题
        builder.setTitle(title);
 
        // 设置列表项
        builder.setItems(items.toArray(new CharSequence[0]), (dialog, which) -> {
            // 获取用户选择的项
            String selectedItem = items.get(which);
 
            // 复制文本到剪贴板
            ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData clip = ClipData.newPlainText("label", selectedItem);
            clipboard.setPrimaryClip(clip);
 
            // 可选: 提示用户已成功复制
            Toast.makeText(context, "已复制: " + selectedItem, Toast.LENGTH_SHORT).show();
 
            // 关闭对话框
            dialog.dismiss();
        });
 
        // 创建并显示对话框
        AlertDialog dialog = builder.create();
        dialog.show();
    }
}

代码很简单不多解释  无非是添加隐藏 

到此这篇关于使用Android实现跨页面悬浮窗效果的文章就介绍到这了,更多相关Android跨页面悬浮窗内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • android popwindow实现左侧弹出菜单层及PopupWindow主要方法介绍

    android popwindow实现左侧弹出菜单层及PopupWindow主要方法介绍

    PopupWindow可以实现浮层效果,主要方法有:可以自定义view,通过LayoutInflator方法;可以出现和退出时显示动画;可以指定显示位置等感兴趣的朋友可以了解下哦,希望本文对你学习android菜单相关开发有所帮助
    2013-01-01
  • Flutter开发之Shortcuts快捷键组件的用法详解

    Flutter开发之Shortcuts快捷键组件的用法详解

    在桌面端的开发中,键盘快捷键是非常常见而必要的,Flutter 既然可以开发桌面端应用,那必然要提供自定义快捷键,所以本文就来和大家讲讲Shortcuts组件的简单使用吧
    2023-05-05
  • Android项目实战教程之高仿网易云音乐启动页实例代码

    Android项目实战教程之高仿网易云音乐启动页实例代码

    这篇文章主要给大家介绍了关于Android项目实战教程之高仿网易云音乐启动页的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-09-09
  • Android图片缓存原理、特性对比

    Android图片缓存原理、特性对比

    这篇文章主要为大家详细介绍了Android图片缓存原理、特性对比 ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • java & Android 格式化字符串详解

    java & Android 格式化字符串详解

    这篇文章主要介绍了java & Android 格式化字符串详解的相关资料,需要的朋友可以参考下
    2016-09-09
  • Android拨打电话功能实例详解

    Android拨打电话功能实例详解

    这篇文章主要介绍了Android拨打电话功能,结合实例形式较为详细的分析了Android实现拨打电话功能的具体步骤与功能代码,具有一定参考借鉴价值,需要的朋友可以参考下
    2016-02-02
  • Android 中隐藏虚拟按键的方法实例代码

    Android 中隐藏虚拟按键的方法实例代码

    本文通过实例代码给大家详细介绍了android隐藏虚拟按键的方法,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2016-12-12
  • Android判断Activity是否在最上层的方法

    Android判断Activity是否在最上层的方法

    这篇文章主要介绍了Android判断Activity是否在最上层的方法,涉及Android针对Activity属性判断与操作相关技巧,代码非常简单易懂,需要的朋友可以参考下
    2016-01-01
  • Android中注解处理器APT用法示例

    Android中注解处理器APT用法示例

    APT全称Annotation Processing Tool,即注解处理器,APT是一种处理注释的工具, 它对源代码文件进行检测找出其中的注解,并使用注解进行额外的处理,给我们自动生成代码,简化使用,很多流行框架都使用到了APT技术,如 ButterKnife,Retrofit,Arouter,EventBus 等等
    2023-12-12
  • Android 实现自定义折线图控件

    Android 实现自定义折线图控件

    这篇文章主要介绍了Android 实现自定义折线图控件,文章围绕主题相关内容展开详细的内容介绍,具有一定的参考价值,更兴趣的小伙伴可以参考一下
    2022-06-06

最新评论