Flutter 实现安卓原生系统级悬浮窗

2023-11-09

Flutter实现安卓原生系统级悬浮窗

原创:@As.Kai
博客地址:https://blog.csdn.net/qq_42362997
如果以下内容对您有帮助,点赞点赞点赞~

最近碰到了一个需求 使用Flutter实现悬浮窗效果
想来想去只能使用原生代码实现 需求整理:

应用移动到后台 -> 显示系统级悬浮窗口
应用移动到前台 -> 关闭系统级悬浮窗口
点击悬浮窗 显示占比30%的窗口 并且监听剪贴板
获取剪贴板内容请求调用后端接口
显示下半布局 整个窗口改为占80%高度 显示相应内容

效果图:

请添加图片描述 请添加图片描述 请添加图片描述

效果图大概是上面三张的效果:

点击议价小圈->获取剪贴板内容并且set到文本框上->利用获取到的内容请求接口获取识别内容set到文本框下方TextView上

有数据时点击议价->显示下面内容布局 下面数据布局使用的 横向 RecyclerView 竖向RecyclerView

点击右上角折叠按钮 缩小窗口到议价小圈

代码思路展示:

首先 找到目录文件…/android/app/src/main/java/xx/xx/xx/MainActivity.java文件
xx/xx/xx为您的项目名称 通常使用项目域名倒序命名

在MainActivity.java中重写configureFlutterEngine()方法
并在其中注册FlutterEngine
添加Method建立Flutter与原生通信通道检查悬浮窗权限内容
如果没有权限引导用户到系统设置页面 手动打开

Method在这里我就不细说了 有需要的可以看看我之前写的文章

public static Context mContext;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mContext = this;
}

@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    GeneratedPluginRegistrant.registerWith(flutterEngine);
    setTokenChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), channelKey);
    setTokenChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
        @Override
        public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
            switch (call.method) {
                case "checkWindowPermission":
                    if(canShowOnce == 0){
                        canShowOnce++;
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(MainActivity.this)) {
                            //没有权限,需要申请权限,因为是打开一个授权页面,所以拿不到返回状态的,所以建议是在onResume方法中从新执行一次校验
                            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                            intent.setData(Uri.parse("package:" + getPackageName()));
                            startActivityForResult(intent, 100);
                        }
                    }


                    break;
            }
        }
    });
}

重写onActivityResult方法 获取用户是否开启权限
并且在onstop中打开悬浮窗
onResume关闭悬浮窗服务

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 0) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

@Override
protected void onResume() {
    SharedPreferences sp = getSharedPreferences("token", MODE_PRIVATE);
    if (sp.getString("haveToken", "default value") != null) {
        stopService(new Intent(MainActivity.this, FloatingService.class));
    }
    super.onResume();
}

@Override
protected void onStop() {
    SharedPreferences sp = getSharedPreferences("token", MODE_PRIVATE);
    if (sp.getString("haveToken", "default value") != null) {
        startFloatingButtonService();
    }
    super.onStop();
}

public void startFloatingButtonService() {
    startService(new Intent(MainActivity.this, FloatingService.class));
}

接着创建悬浮窗服务文件:FloatingService.java继承Service
在onCreate方法中拿悬浮窗服务初始化
updateLayoutParams()方法是封装出来 调整悬浮窗宽高度/xy轴定位以及类型之类的
大家感兴趣可以看看源码

private WindowManager.LayoutParams mainParams;
private WindowManager.LayoutParams floatWindowLayoutParam;
private WindowManager windowManager;

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    mainParams = new WindowManager.LayoutParams();
    updateLayoutParams(mainParams);
}

private void updateLayoutParams(WindowManager.LayoutParams layoutParams) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    } else {
        layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
    }
    layoutParams.format = PixelFormat.RGBA_8888;
    layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
    layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    layoutParams.width = ScreenUtils.dp2px(66);
    layoutParams.height = ScreenUtils.dp2px(66);
    layoutParams.x = ScreenUtils.getRealWidth() - ScreenUtils.dp2px(60);
    layoutParams.y = ScreenUtils.deviceHeight() / 10 * 2;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    showFloatingWindow();
    return super.onStartCommand(intent, flags, startId);
}

onStartCommand也就是显示悬浮窗的地方 里面放的一般是悬浮窗布局/样式
这里的button就是效果图中议价小圆圈的样式了 drawable样式文件我就不放出来
可以根据自己的实现效果自定义效果

通过点击议价小圆圈 显示上半部分布局 并且隐藏小圆圈布局
button.setVisibility(View.GONE);

windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
LayoutInflater inflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
floatView = (ViewGroup) inflater.inflate(R.layout.floating_layout, null);
利用ViewGroup绑定上半+下半布局 layout文件

private Button button;
private ViewGroup floatView;
private LinearLayout bodyLinear;
private ClipboardManager manager;

private void showFloatingWindow() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//判断系统版本
            if (Settings.canDrawOverlays(this)) {
                button = new Button(getApplicationContext());
                button.setText("询价");
                button.setBackgroundResource(R.drawable.button_style);
                windowManager.addView(button, mainParams);
                button.setOnClickListener(new View.OnClickListener() {
                    ///议价按钮点击
                    @Override
                    public void onClick(View view) {
                        button.setVisibility(View.GONE);
                        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
                        LayoutInflater inflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
                        floatView = (ViewGroup) inflater.inflate(R.layout.floating_layout, null);
                        bodyLinear = floatView.findViewById(R.id.body_dialog);
                        descEditArea = floatView.findViewById(R.id.float_edit);
                        descEditArea.setSelection(descEditArea.getText().toString().length());
                        descEditArea.setCursorVisible(false);
                        bottomWidget = floatView.findViewById(R.id.bottom_widget);
                        bottomRecyclerView = floatView.findViewById(R.id.listview_horizontial);
                        planRecyclerView = floatView.findViewById(R.id.plan_recyclerView);
                        bottomLinear = floatView.findViewById(R.id.recycler_view_linear);
                        systemIden = floatView.findViewById(R.id.system_iden);
                        hintButton = floatView.findViewById(R.id.hint_button);
                        returnAppText = floatView.findViewById(R.id.return_app_text);
                        floatView.requestFocus();
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            LAYOUT_TYPE = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
                        } else {
                            LAYOUT_TYPE = WindowManager.LayoutParams.TYPE_TOAST;
                        }

                        //这里用来控制上半布局属性
                        floatWindowLayoutParam = new WindowManager.LayoutParams(
                                ScreenUtils.getRealWidth() / 10 * 9,
                                ScreenUtils.deviceHeight() / 10 * 3,
                                LAYOUT_TYPE,
                                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                                PixelFormat.TRANSLUCENT
                        );


                        floatWindowLayoutParam.gravity = Gravity.CENTER;
                        floatWindowLayoutParam.x = 0;
                        floatWindowLayoutParam.y = -ScreenUtils.deviceHeight() / 10 * 2;

                        //添加到windowManager中
                        windowManager.addView(floatView, floatWindowLayoutParam);

                        //点击TextView回到App中 xx.xx.xx为您的包名
                        returnAppText.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                Intent intent = getPackageManager().getLaunchIntentForPackage(“xx.xx.xx");
                                startActivity(intent);
                            }
                        });

                        //获取剪贴板内容
                        manager = (ClipboardManager) getSystemService(getApplicationContext().CLIPBOARD_SERVICE);
                        if (manager != null) {
                            if (manager.hasPrimaryClip()) {
                                if (manager.getPrimaryClip().getItemCount() > 0) {
                                    CharSequence addedText = manager.getPrimaryClip().getItemAt(0).getText();
                                    String addedTextString = String.valueOf(addedText);
                                    //拿到剪贴板内容 setText 并且使用Runnable刷新控件
                                    descEditArea.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            descEditArea.setText(addedTextString);
                                        }
                                    });
                                }
                            }
                        }
                        //监听剪贴板 如果剪贴板内容有改变 重新赋值到文本框内
                        manager.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
                            @Override
                            public void onPrimaryClipChanged() {
                                CharSequence addedText = manager.getPrimaryClip().getItemAt(0).getText();
                                String addedTextString = String.valueOf(addedText);
                                descEditArea.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        descEditArea.setText(addedTextString);
                                    }
                                });
                            }
                        });
                        //折叠上半+下半布局 回到议价小圆圈
                        hintButton.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                button.setVisibility(View.VISIBLE);
                                bodyLinear.setVisibility(View.GONE);
                                windowManager.updateViewLayout(floatView, floatWindowLayoutParam);
                            }
                        });


                        clickShowBottom = floatView.findViewById(R.id.click_show_bottom_text);
                        ///点击询价
                        clickShowBottom.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                //如果请求接口获取到的数据不为空 显示下半布局
                                if (data != null && data.size() > 0) {
                                    if (bottomWidget.getVisibility() != View.VISIBLE) {
                                        bottomWidget.setVisibility(View.VISIBLE);
                                        floatWindowLayoutParam.height = ScreenUtils.deviceHeight() / 10 * 7;
                                        floatWindowLayoutParam.gravity = Gravity.CENTER;
                                        floatWindowLayoutParam.x = 0;
                                        floatWindowLayoutParam.y = ScreenUtils.dp2px(10);
                                        LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
                                        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
                                        //下半底部横向滚动布局 RecyclerView 
                                        bottomRecyclerView.setLayoutManager(layoutManager);
                                        adapter = new RecyclerAdapter(bargains);
                                        bottomRecyclerView.setAdapter(adapter);
                                        adapter.notifyDataSetChanged();

                                        //下半布局竖向滚动布局RecyclerView
                                        LinearLayoutManager planLayoutManager = new LinearLayoutManager(getApplicationContext());
                                        planLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
                                        planRecyclerView.setLayoutManager(planLayoutManager);
                                        PlanAdapter planAdapter = new PlanAdapter(plans);
                                        planRecyclerView.setAdapter(planAdapter);


                                        windowManager.updateViewLayout(floatView, floatWindowLayoutParam);
                                        adapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {
                                            @Override
                                            public void onItemClick(View view, int position) {
                                                Intent intent = getPackageManager().getLaunchIntentForPackage("com.shibida.flutter_purchase");
                                                startActivity(intent);
                                            }
                                        });
                                    }
                                } else {
                                    Toast.makeText(getApplicationContext(), "请先粘贴内容识别", Toast.LENGTH_SHORT).show();
                                }




                            }
                        });


                        descEditArea.addTextChangedListener(new TextWatcher() {
                            @Override
                            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                                //Not Necessary
                            }


                            @Override
                            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                                ///调用内容
                                if (!descEditArea.getText().toString().equals("")) {
                                    queryData(descEditArea.getText().toString());
                                }


                            }


                            @Override
                            public void afterTextChanged(Editable editable) {
                                //Not Necessary
                            }
                        });


                        floatView.setOnTouchListener(new View.OnTouchListener() {
                            final WindowManager.LayoutParams floatWindowLayoutUpdateParam = floatWindowLayoutParam;
                            double x;
                            double y;
                            double px;
                            double py;


                            @Override
                            public boolean onTouch(View v, MotionEvent event) {


                                switch (event.getAction()) {
                                    //When the window will be touched, the x and y position of that position will be retrieved
                                    case MotionEvent.ACTION_DOWN:
                                        x = floatWindowLayoutUpdateParam.x;
                                        y = floatWindowLayoutUpdateParam.y;
                                        //returns the original raw X coordinate of this event
                                        px = event.getRawX();
                                        //returns the original raw Y coordinate of this event
                                        py = event.getRawY();
                                        break;
                                    //When the window will be dragged around, it will update the x, y of the Window Layout Parameter
                                    case MotionEvent.ACTION_MOVE:
                                        floatWindowLayoutUpdateParam.x = (int) ((x + event.getRawX()) - px);
                                        floatWindowLayoutUpdateParam.y = (int) ((y + event.getRawY()) - py);


                                        //updated parameter is applied to the WindowManager
                                        windowManager.updateViewLayout(floatView, floatWindowLayoutUpdateParam);
                                        break;
                                }


                                return false;
                            }
                        });


                        descEditArea.setOnTouchListener(new View.OnTouchListener() {
                            @Override
                            public boolean onTouch(View v, MotionEvent event) {
//                                ClipboardManager manager = getApplicationContext().getSystemService()
                                descEditArea.setCursorVisible(true);
                                WindowManager.LayoutParams floatWindowLayoutParamUpdateFlag = floatWindowLayoutParam;
                                floatWindowLayoutParamUpdateFlag.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
                                windowManager.updateViewLayout(floatView, floatWindowLayoutParamUpdateFlag);
                                return false;
                            }
                        });
                    }
                });
                button.setOnTouchListener(new FloatingOnTouchListener());
            }
        }
    }
@Override
public void onDestroy() {
    super.onDestroy();
    stopSelf();
    //Window is removed from the screen
    if (button != null) {
        windowManager.removeView(button);
    }
    if (floatView != null) {
        windowManager.removeView(floatView);
    }
}

//悬浮窗移动
private class FloatingOnTouchListener implements View.OnTouchListener {
    private int x;
    private int y;
    private long downTime;
    public int positionX;
    public int positionY;


    @Override
    public boolean onTouch(View view, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downTime = System.currentTimeMillis();
                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int nowX = (int) event.getRawX();
                int nowY = (int) event.getRawY();
                int movedX = nowX - x;
                int movedY = nowY - y;
                x = nowX;
                y = nowY;
                mainParams.x = positionX != 0 ? positionX : mainParams.x + movedX;
                positionX = mainParams.x + movedX;
                mainParams.y = positionY != 0 ? positionY : mainParams.y + movedY;
                positionY = mainParams.y + movedY;
                windowManager.updateViewLayout(view, mainParams);
                break;
            default:
                break;
        }
        return false;
    }
}

这里我就不放okHttp3请求接口的内容了
思路就是在请求接口返回200时
将数据放到Adapter中并且刷新RecyclerView控件
未识别到内容时 将下半部分布局隐藏

最后别忘了在AndroidManifest.xml中添加一下内容:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
……….
<service
    android:name=".FloatingService"
    tools:ignore="Instantiatable" />

在写完代码之后有遇到一个问题,在应用后台显示悬浮窗拿不到焦点
最后查阅文章时在找到解决办法
是因为之前设置WindowManager.LayoutParams属性时设置为了FLAG_NOT_FOCUSABLE后改为FLAG_LAYOUT_IN_SCREEN后解决问题

使用机型:HONOR 20 Android 10 SDK29
大概就是这样 所有内容都放在一个文件中方便大家查阅,如果有遇到哪些问题可以私信给我 或者留言

关注我,一起成长!
@As.Kai

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter 实现安卓原生系统级悬浮窗 的相关文章

  • Eclipse Oxygen - 该项目未构建,因为其构建路径不完整

    我刚刚安装了 Eclipse Oxygen 并尝试在工作台中打开现有项目 但收到此错误 该项目未构建 因为其构建路径不完整 不能 找到 java lang Object 的类文件 修复构建路径然后尝试 建设这个项目 我尝试右键单击该项目 转
  • JaCoCo 显示 0% 覆盖率,即使所有测试均已通过

    我在下面写了一些测试用例androidTest目录 我正在使用 androidx testInstrumentationRunner androidx test runner AndroidJUnitRunner 添加的依赖项 androi
  • Firestore OncompleteListener [重复]

    这个问题在这里已经有答案了 我想看看这段代码的执行有什么错误 当我编译它时 它只返回 log 1 3 2 的值 并且我希望 log2 在 3 之前 Log d 1 antes de validar DocumentReference doc
  • Android - 带动画的可扩展 TextView

    我有一个TextView首先显示长文本的一小部分 用户可以按 查看更多 按钮来展开TextView并查看该文本的其余部分 进行测试 我可以通过简单地交换以下值来实现这一点TextView setMaxLines介于 4 之间 用于折叠 和
  • 安卓浮动键盘

    是否可以通过编程方式更改键盘的位置 我正在开发一个图腾应用程序 如果键盘停靠在底部 则很难使用 我尝试创建一个扩展 InputMethodService 的自定义键盘 并且我能够更改按键的布局 但我无法更改键盘的视图位置 快捷键 https
  • 用于 Eclipse 的 Resharper [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 自定义支持对话框片段文本颜色错误

    如果我放一个TextView在自定义对话框中 文本默认为黑色 不幸的是 支持片段对话框背景是深灰色的 灰底黑字很难读 我可以改变TextView白色或类似的东西android textColor android attr textColor
  • Log4j 2.0 中发现 ClassNotFoundException

    我已经设置了 log4j12 api beta2 jar 的构建路径 但它给出了 以下错误请帮我解决这个问题我的代码如下 java 文件 package com sst log4j class Product private int pro
  • androidx.navigation.fragment.NavHostFragment 无法从 xml 文件访问

    我正在尝试使用带有底部导航视图的 androidx 导航 因此 当我在 xml 文件中放置带有 android name androidx navigation fragment NavHostFragment 的片段时 它会给我一个错误
  • IntelliJ - 无效源版本:17

    我已经在 IntelliJ 中使用 Gradle 创建了一个使用 Java 17 的新 Java 项目 运行我的应用程序时出现错误Cause error invalid source release 17 我的设置 我已经安装了openjd
  • SQlite 获取最近的位置(带有纬度和经度)

    我的 SQLite 数据库中存储有纬度和经度的数据 我想获取距我输入的参数最近的位置 例如我当前的位置 纬度 经度等 我知道这在 MySQL 中是可能的 并且我已经做了相当多的研究 SQLite 需要一个自定义外部函数来实现半正弦公式 计算
  • 在 Apache Servicemix 4 中的 OSGi 包之间共享配置文件?

    有人能够在 SMX4 中的两个或多个捆绑包之间成功共享配置吗 我正在寻找的是这样的 有一个文件 SMX HOME etc myconfiguration cfg 使此配置 可用 以便使用 Spring dm 通过 OSGi 配置管理将其注入
  • 带等待/通知的同步块与不带等待/通知的同步块之间的区别?

    如果我只是使用synchronized 不是wait notify方法 它仍然是线程安全的吗 有什么不同 Using synchronized使方法 块一次只能由一个线程访问 所以 是的 它是线程安全的 这两个概念是结合在一起的 而不是相互
  • “强制更新快照/版本” - 这是什么意思

    在 Maven 项目中 选择 更新项目 时 有一个名为 强制更新快照 版本 的选项 它有什么作用 强制更新快照 版本 就像运行以下命令 mvn U install U 也可以用作 update snapshot 看here http boo
  • 对于双核手机,availableProcessors() 返回 1

    我最近购买了一部 Moto Atrix 2 手机 当我尝试查看手机中的处理器规格时 Runtime getRuntime availableProcessors 返回 1 proc cpuinfo 也仅包含有关处理器 0 的信息 出于好奇
  • Android - 从渲染线程内结束活动

    下午好 我不熟悉 android 中的活动生命周期 并且一直在尽可能地阅读 但我不知道如何以良好的方式解决以下问题 我有一个使用 GLSurfaceView 的活动来在屏幕上绘制各种内容 在这个 GLSurfaceView 的渲染线程中 我
  • java中什么是静态接口?

    我正在阅读Map Entry界面 当我注意到它是一个static界面 我不太明白什么是静态接口 它与常规接口有什么不同 public static interface Map Entry
  • Volley 在第一次调用方法时返回 null

    我正在尝试使用 volley 从服务器检索数据 但是当我第一次调用此方法时 我收到服务器的响应 但该方法返回 null 如果我第二次调用它 我会得到最后的响应 public String retrieveDataFromServer Str
  • 将其元素添加到另一个列表后清除列表

    我正在做一个程序 它获取更多句子作为参数 我制作了 2 个列表 一个称为 propozitie 其中包含每个句子 另一个称为 propozitii 其中包含所有句子 问题是 当我在遇到 后清除 propozitie 列表时 它也会清除 pr
  • removeall 和removeif 的用例

    我找到了这个 fun main val list MutableList

随机推荐

  • 替代for循环,让Python代码更pythonic

    通常如下使用场景中会用到 for 循环 在一个序列来提取一些信息 从一个序列生成另一个序列 写 for 已成习惯 幸运的是 Python 已经有很多工具可以帮助你完成这些工作 你只需要转移你的思路 并以不同的角度来思考它 通过避免编写 fo
  • Pycharm更换python解释器

    Pycharm工程如何更换pycharm解析器 Mac Pycharm gt Preferences 然后会出来如下界面 Windows file gt Default Settings
  • leetcode 300. 最长递增子序列

    2023 8 23 本题也是dp算法解决的经典题型 dp i 的含义 数组前i个元素的最长递增子序列长度 不多说 直接上代码 class Solution public int lengthOfLIS vector
  • 为什么Git用SHA做版本控制,而非像SVN用int数字或者是时间戳

    在分布式领域中 做的最出色的莫过于Git 而Git主要强大之处就在于运用sha作为版本控制的算法 而许多分布式架构的项目也都意义效仿 而sha算法为什么如此神奇呢 SHA是什么 百度百科定义是 安全散列算法 英语 Secure Hash A
  • pycharm+gitee安装与配置

    下载安装 1 下载地址 https www jetbrains com pycharm download section windows 选择这个免费版进行安装 2 自定义安装路径 3 根据自己的需求选择项 点击下一步 一步步的安装下去就行
  • please select a valid python interpreter(请选择一个解释器)

    1 更改Pycharm的设置 打开settings CTRL ALT S 或者file gt settings 2 在查询框中输interpreter 进行查询 3 选择界面右边上的配置按钮 右击选择add local 4 选择system
  • Linux服务器成功安装CUDA11.3和cudnn

    说在前面 NVIDIA的显卡驱动与CUDA的版本并不是严格的一一对应关系 CUDA实际上也只是一个工具包 我们可以根据自己的需求进行安装 即可以安装多个CUDA版本 同时CUDNN是一个SDK 专门用于神经网络的加速包 它与CUDA也没有严
  • 巨细!Python爬虫详解

    Python技术 爬虫 又称为网页蜘蛛 网络机器人 在 FOAF 社区中间 更经常的称为网页追逐者 它是一种按照一定的规则 自动地抓取网络信息的程序或者脚本 如果我们把互联网比作一张大的蜘蛛网 那一台计算机上的数据便是蜘蛛网上的一个猎物 而
  • 第十一章 GetAway服务网关详解

    目录 一 服务网关使用背景 二 服务网关的种类和区别 三 Gateway 简介 优点 缺点 Gateway组成 三 快速入门案例 1 创建网关服务 引入 GetAway 依赖 2 创建主类 3 配置GetAway网关服务 4 创建 订单服务
  • PyCharm安装教程,图文教程(超详细)

    作者简介 CSDN top100 阿里云博客专家 华为云享专家 网络安全领域优质创作者 推荐专栏 对网络安全感兴趣的小伙伴可以关注专栏 网络安全入门到精通 PyCharm 一 PyCharm下载安装 二 Python下载安装 三 创建项目
  • Unity点击物体后,移动到物体所在位置

    Unity点击物体后 移动到物体所在位置 方法一 OnMouse检测 需要Collider组件 脚本挂在被点击的物体上 using System Collections Generic using UnityEngine using Uni
  • 【Python】字典dict 基础用法

    参考资料 Python字典 Dictionary 操作全解 创建 读取 修改 添加 删除 有序字典 浅复制 排序 字典是 键 值 对的无序可变序列 字典中的每个元素可以分为两部分 键 和 值 定义字典时 每个元素的 键 和 值 用冒号分隔
  • 朴素贝叶斯分类器

    贝叶斯分类器 优点 数据较少任然有效 可以处理多类别的问题 缺点 输入数据准备方式比价敏感 数据类型 标称型数据 朴素贝叶斯决策理论 假设有两类数据 A 和 B 假设有两类数据 A和B 假设有两类数据 A和B 平面直角坐标
  • springboot开发环境生产环境配置文件切换

    一 单个文件切换环境里 在application yml配置文件中添加如下信息 server port 8081 spring profiles active prod 激活prod生产环境 server port 8083 spring
  • “物联网+区块链”技术给农业发展带来新机遇

    运用 物联网 区块链 技术 从鸡苗供应 养殖 再到屠宰 流通等环节 每一只肉鸡的信息数据都被如实记录在 身份证 上 且不可篡改 经区块链加密的多宝鱼生长数据 通过 绿色履历 呈现在消费者面前 从土地承包到播种 加工 稻米的相关信息全部在区块
  • 【机器学习实战】5、Logistic 回归

    文章目录 5 1 基于Logistic回归和Sigmoid函数的分类 5 2 基于最优化方法的最佳回归系数确定 5 2 1 梯度上升法 5 3 python实战 5 3 1 查看数据集分布情况 5 3 2 训练 5 3 3 绘制决策边界 5
  • OpenCV相机标定全过程

    一 OpenCV标定的几个常用函数 findChessboardCorners 棋盘格角点检测 bool findChessboardCorners InputArray image Size patternSize OutputArray
  • Go语言在机器学习中有未来吗?

    Go 是一种开源编程语言 最初由 Google 设计 用于优化系统级服务的构建和使用 在大型代码库上轻松工作 以及利用多核联网机器 Go 于 2009 年推出 作为一种静态类型和编译型编程语言 深受 C 语言的影响 注重简单性 安全性和并发
  • ros 源码安装

    版本lunar 系统版本debian 9 8 参考 http wiki ros org lunar Installation Source 1 Installing bootstrap dependencies sudo apt get i
  • Flutter 实现安卓原生系统级悬浮窗

    Flutter实现安卓原生系统级悬浮窗 原创 As Kai 博客地址 https blog csdn net qq 42362997 如果以下内容对您有帮助 点赞点赞点赞 最近碰到了一个需求 使用Flutter实现悬浮窗效果 想来想去只能使