android自定义来电秀UI

news2024/9/21 14:40:54

简单来电秀功能,效果如图:

底部附上demo 

一、新建一个PhoneCallService服务,在服务中监听来电等状态,且控制UI显示

public class PhoneCallService extends InCallService {

    private final Call.Callback callback = new Call.Callback() {
        @Override
        public void onStateChanged(Call call, int state) {
            super.onStateChanged(call, state);

            switch (state) {
                case Call.STATE_ACTIVE: {

                    break;
                }

                case Call.STATE_DISCONNECTED: {
                    ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);
                    break;
                }

            }
        }
    };

    @Override
    public void onCallAdded(Call call) {
        super.onCallAdded(call);

        call.registerCallback(callback);
        PhoneCallManager.call = call;

        CallType callType = null;

        if (call.getState() == Call.STATE_RINGING) {
            callType = CallType.CALL_IN;
        } else if (call.getState() == Call.STATE_CONNECTING) {
            callType = CallType.CALL_OUT;
        }

        if (callType != null) {
            Call.Details details = call.getDetails();
            String phoneNumber = details.getHandle().getSchemeSpecificPart();
            PhoneCallActivity.actionStart(this, phoneNumber, callType);
        }
    }

    @Override
    public void onCallRemoved(Call call) {
        super.onCallRemoved(call);

        call.unregisterCallback(callback);
        PhoneCallManager.call = null;
    }

    public enum CallType {
        CALL_IN,
        CALL_OUT,
    }

二、在MainActivity中设置应用为默认来电主题,与权限设置

public class MainActivity extends AppCompatActivity {

    @SuppressLint("UseSwitchCompatOrMaterialCode")
    private Switch switchPhoneCall;

    @SuppressLint("UseSwitchCompatOrMaterialCode")
    private Switch switchListenCall;

    private CompoundButton.OnCheckedChangeListener switchCallCheckChangeListener;

    private final ActivityResultLauncher<Intent> dialerRequestLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(), result -> {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
                            Toast.LENGTH_SHORT).show();
                }
            });

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView() {
        switchPhoneCall = findViewById(R.id.switch_default_phone_call);
        switchListenCall = findViewById(R.id.switch_call_listenr);

        switchPhoneCall.setOnClickListener(v -> {
            // 发起将本应用设为默认电话应用的请求,仅支持 Android M 及以上
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (switchPhoneCall.isChecked()) {
                    // Android 10 之后需要通过 RoleManager 修改默认电话应用
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
                        RoleManager roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE);
                        Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
                        dialerRequestLauncher.launch(intent);
                    } else {
                        Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
                        intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, getPackageName());
                        startActivity(intent);
                    }
                } else {
                    // 取消时跳转到默认设置页面
                    startActivity(new Intent("android.settings.MANAGE_DEFAULT_APPS_SETTINGS"));
                }
            } else {
                Toast.makeText(MainActivity.this, "Android 6.0 以上才支持修改默认电话应用!", Toast.LENGTH_LONG)
                     .show();
                switchPhoneCall.setChecked(false);
            }

        });

        // 检查是否开启了权限
        switchCallCheckChangeListener = (buttonView, isChecked) -> {
            if (isChecked && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                    && !Settings.canDrawOverlays(MainActivity.this)) {
                // 请求 悬浮框 权限
                askForDrawOverlay();

                // 未开启时清除选中状态,同时避免回调
                switchListenCall.setOnCheckedChangeListener(null);
                switchListenCall.setChecked(false);
                switchListenCall.setOnCheckedChangeListener(switchCallCheckChangeListener);
                return;
            }

            if (isChecked && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
                    ContextCompat.checkSelfPermission(this,
                            Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "缺少获取电话状态权限", Toast.LENGTH_SHORT).show();
                return;
            }

            Intent callListener = new Intent(MainActivity.this, CallListenerService.class);
            if (isChecked) {
                startService(callListener);
                Toast.makeText(this, "电话监听服务已开启", Toast.LENGTH_SHORT).show();
            } else {
                stopService(callListener);
                Toast.makeText(this, "电话监听服务已关闭", Toast.LENGTH_SHORT).show();
            }
        };
        switchListenCall.setOnCheckedChangeListener(switchCallCheckChangeListener);
    }

    private void askForDrawOverlay() {
        AlertDialog alertDialog = new AlertDialog.Builder(this)
                .setTitle("允许显示悬浮框")
                .setMessage("为了使电话监听服务正常工作,请允许这项权限")
                .setPositiveButton("去设置", (dialog, which) -> {
                    openDrawOverlaySettings();
                    dialog.dismiss();
                })
                .setNegativeButton("稍后再说", (dialog, which) -> dialog.dismiss())
                .create();

        //noinspection ConstantConditions
        alertDialog.getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
        alertDialog.show();
    }

    /**
     * 跳转悬浮窗管理设置界面
     */
    private void openDrawOverlaySettings() {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // Android M 以上引导用户去系统设置中打开允许悬浮窗
            // 使用反射是为了用尽可能少的代码保证在大部分机型上都可用
            try {
                Context context = this;
                Class clazz = Settings.class;
                Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
                Intent intent = new Intent(field.get(null).toString());
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.setData(Uri.parse("package:" + context.getPackageName()));
                context.startActivity(intent);
            } catch (Exception e) {
                Toast.makeText(this, "请在悬浮窗管理中打开权限", Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        switchPhoneCall.setChecked(isDefaultPhoneCallApp());
        switchListenCall.setChecked(isServiceRunning(CallListenerService.class));
    }

    /**
     * Android M 及以上检查是否是系统默认电话应用
     */
    public boolean isDefaultPhoneCallApp() {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
            if (manger != null && manger.getDefaultDialerPackage() != null) {
                return manger.getDefaultDialerPackage().equals(getPackageName());
            }
        }
        return false;
    }

    public boolean isServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        if (manager == null) return false;

        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
                Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }

        return false;
    }

三、增加一个来电展示的自定义UI页面PhoneCallActivity,可根据需求更改

public class PhoneCallActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView tvCallNumberLabel;
    private TextView tvCallNumber;
    private TextView tvPickUp;
    private TextView tvCallingTime;
    private TextView tvHangUp;

    private PhoneCallManager phoneCallManager;
    private PhoneCallService.CallType callType;
    private String phoneNumber;

    private Timer onGoingCallTimer;
    private int callingTime;

    public static void actionStart(Context context, String phoneNumber,
                                   PhoneCallService.CallType callType) {
        Intent intent = new Intent(context, PhoneCallActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(Intent.EXTRA_MIME_TYPES, callType);
        intent.putExtra(Intent.EXTRA_PHONE_NUMBER, phoneNumber);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_phone_call);

        ActivityStack.getInstance().addActivity(this);

        initData();

        initView();
    }

    private void initData() {
        phoneCallManager = new PhoneCallManager(this);
        onGoingCallTimer = new Timer();
        if (getIntent() != null) {
            phoneNumber = getIntent().getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            callType = (PhoneCallService.CallType) getIntent().getSerializableExtra(Intent.EXTRA_MIME_TYPES);
        }
    }

    private void initView() {
        int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        getWindow().getDecorView().setSystemUiVisibility(uiOptions);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

        tvCallNumberLabel = findViewById(R.id.tv_call_number_label);
        tvCallNumber = findViewById(R.id.tv_call_number);
        tvPickUp = findViewById(R.id.tv_phone_pick_up);
        tvCallingTime = findViewById(R.id.tv_phone_calling_time);
        tvHangUp = findViewById(R.id.tv_phone_hang_up);

        tvCallNumber.setText(formatPhoneNumber(phoneNumber));
        tvPickUp.setOnClickListener(this);
        tvHangUp.setOnClickListener(this);

        // 打进的电话
        if (callType == PhoneCallService.CallType.CALL_IN) {
            tvCallNumberLabel.setText("来电号码");
            tvPickUp.setVisibility(View.VISIBLE);
        }
        // 打出的电话
        else if (callType == PhoneCallService.CallType.CALL_OUT) {
            tvCallNumberLabel.setText("呼叫号码");
            tvPickUp.setVisibility(View.GONE);
            phoneCallManager.openSpeaker();
        }

        showOnLockScreen();
    }

    public void showOnLockScreen() {
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
                        WindowManager.LayoutParams.FLAG_FULLSCREEN |
                        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
                        WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
                        WindowManager.LayoutParams.FLAG_FULLSCREEN |
                        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
                        WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.tv_phone_pick_up) {
            phoneCallManager.answer();
            tvPickUp.setVisibility(View.GONE);
            tvCallingTime.setVisibility(View.VISIBLE);
            onGoingCallTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    runOnUiThread(new Runnable() {
                        @SuppressLint("SetTextI18n")
                        @Override
                        public void run() {
                            callingTime++;
                            tvCallingTime.setText("通话中:" + getCallingTime());
                        }
                    });
                }
            }, 0, 1000);
        } else if (v.getId() == R.id.tv_phone_hang_up) {
            phoneCallManager.disconnect();
            stopTimer();
        }
    }

    private String getCallingTime() {
        int minute = callingTime / 60;
        int second = callingTime % 60;
        return (minute < 10 ? "0" + minute : minute) +
                ":" +
                (second < 10 ? "0" + second : second);
    }

    private void stopTimer() {
        if (onGoingCallTimer != null) {
            onGoingCallTimer.cancel();
        }

        callingTime = 0;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        phoneCallManager.destroy();
    }

四、来电页面UI中布局文件activity_phone_call

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@mipmap/sf_p1"
    tools:context=".phonecallui.PhoneCallActivity">

    <RelativeLayout
        android:id="@+id/rl_user_info"
        android:layout_width="match_parent"
        android:layout_height="300dp"
       >

        <TextView
            android:id="@+id/tv_call_number_label"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@+id/tv_call_number"
            android:layout_marginBottom="16dp"
            android:gravity="center"
            android:text="来电号码"

            android:textSize="18sp" />


        <TextView
            android:id="@+id/tv_call_number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textSize="28sp"
            android:textStyle="bold"
            tools:text="130-1111-1111" />

    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
   >

        <TextView
            android:id="@+id/tv_phone_calling_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="24dp"
            android:text="通话中:01:33"
            android:textColor="@android:color/white"
            android:textSize="18sp"
            android:visibility="gone"
            tools:visibility="visible" />

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true">

            <TextView
                android:id="@+id/tv_phone_hang_up"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawablePadding="16dp"
                android:drawableTop="@mipmap/phone_hang_up"
                android:foreground="?android:attr/selectableItemBackground"
                android:gravity="center"
                android:padding="8dp"
                android:text="挂  断"
                android:textColor="@android:color/black"
                tools:visibility="visible" />

            <TextView
                android:id="@+id/tv_phone_pick_up"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="50dp"
                android:layout_toRightOf="@id/tv_phone_hang_up"
                android:drawablePadding="16dp"
                android:drawableTop="@mipmap/phone_pick_up"
                android:foreground="?android:attr/selectableItemBackground"
                android:gravity="center"
                android:padding="8dp"
                android:text="接  听"
                android:textColor="@android:color/black"
                android:visibility="gone"
                tools:visibility="visible" />
        </RelativeLayout>

    </RelativeLayout>
</LinearLayout>

代码为完全贴出,其他代码可下载DEMO查看

                                                                           -END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1381612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

调试(c语言)

前言&#xff1a; 我们在写程序的时候可能多多少少都会出现一些bug&#xff0c;使我们的程序不能正常运行&#xff0c;所以为了更快更好的找到并修复bug&#xff0c;使这些问题迎刃而解&#xff0c;学习好如何调试代码是每个学习编程的人所必备的技能。 1. 什么是bug&#xf…

vue3hooks的使用

在 Vue 3 中&#xff0c;hooks 是用于封装组件逻辑的方法&#xff0c;类似于 Vue 2 中的 mixins。 使用 Hooks 可以提高代码的可维护性、可读性、可复用性和可测试性&#xff0c;降低代码之间的耦合度&#xff0c;使得组件的状态更加可控和可预测。 要使用 hooks&#xff0c;…

数据安全保障的具体措施有哪些

随着信息化时代的到来&#xff0c;数据已经成为企业和社会发展的重要资产。然而&#xff0c;数据安全问题也日益突出&#xff0c;如何保障数据的安全性、完整性和可用性成为了亟待解决的问题。以下将详细探讨数据安全保障的各个方面&#xff0c;以期为企业和社会提供更好的数据…

法律领域AI新突破:Alpha合同审查GPT助力法律效率革新

随着人工智能的发展&#xff0c;众多律师正在智能化工具的帮助下快速提高自身业务处理能力。Alpha系统新推出的合同审查GPT功能&#xff0c;作为法律界AI应用的新选择&#xff0c;已经在法律实践中展现了效率与智能的双重优势。 在日益激烈的市场竞争中&#xff0c;律师需要的…

Java项目:03 基于Springboot的销售培训考评管理系统

项目介绍 企业的销售要进行培训&#xff0c;由技术人员进行辅导并考评检测培训效果&#xff0c;所以有了这个小系统。实现了系统的登录验证、请求拦截验证、基础模块&#xff08;用户管理、角色管理、销售管理&#xff09;、业务模块&#xff08;评分管理、评分结果&#xff0…

ubuntu安装mysql(tar.xz)

0:本机Ubuntu的版本为 腾讯云 18.04 1&#xff1a;下载地址 MySQL &#xff1a;&#xff1a; 下载 MySQL 社区服务器 2&#xff1a;上传文件到服务器 3:解压 sudo sumv mysql-8.2.0-linux-glibc2.17-x86_64-minimal.tar.xz /usrtar -xvf mysql-8.2.0-linux-glibc2.17-x86_6…

Vue2.脚手架

全局安装&#xff1a;npm i vue/cli -g检查是否成功安装&#xff1a;vue --version新建项目&#xff1a;vue create 项目名 通过nodejs安装的时候&#xff0c;可以直接代理和仓库&#xff0c;~/.npmrc文件内容如下&#xff1a; proxysocks5://127.0.0.1:7897 registryhttps:/…

软件测试|使用Python抓取百度新闻的页面内容

简介 作为技术工程师&#xff0c;在繁忙的工作中我们不一定有时间浏览发生的热点新闻&#xff0c;但是懂技术的我们不需要访问网站来看当下发生的大事&#xff0c;我们可以使用网络爬虫的技术来获取当下最新最热的新闻&#xff0c;本文就来介绍一下使用Python抓取一下百度新闻…

【动态规划】【滑动窗口】C++算法:3003 执行操作后的最大分割数量

作者推荐 【动态规划】【字符串】扰乱字符串 本文涉及的基础知识点 C算法&#xff1a;滑动窗口总结 动态规划 LeetCode3003 执行操作后的最大分割数量 给你一个下标从 0 开始的字符串 s 和一个整数 k。 你需要执行以下分割操作&#xff0c;直到字符串 s 变为 空&#xff1…

[情商-11]:人际交流的心理架构与需求层次模型

目录 前言&#xff1a; 一、心理架构 1.1 个体生理层 1.2 个体心理层 1.3 点对点人际交流层 1.4 社会网络层 1.5 社会价值层 二、人的需求层次模型 2.1 需求&#xff08;欲望&#xff09;层次模型 2.2 基因与人需求之间的关系 2.3 个体生理需求 2.4 个体的心理需求…

kylin4.0.3升级问题

话接前文&#xff1a; kylin升级(3.0.1-&#xff1e;kylin-4.0.3)-CSDN博客文章浏览阅读941次&#xff0c;点赞29次&#xff0c;收藏12次。原本的cube太多了&#xff0c;换其他OLAP数据库太麻烦。相比之下&#xff0c;升级是一个很好的选择&#xff08;官网有说明内存降低和构…

计算机毕业设计 基于Java的综合小区管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Java反转单链表

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ //核心思想&#xff0c;利用cur和Curnex…

网络安全B模块(笔记详解)- MYSQL信息收集

MYSQL信息收集 1.通过渗透机场景Kali中的渗透测试工具对服务器场景MySQL03进行服务信息扫描渗透测试(使用工具Nmap,使用必须要使用的参数),并将该操作显示结果中数据库版本信息作为Flag提交; Flag:MySQL 5.5.12 2.通过渗透机场景Kali中的渗透测试工具对服务器场景MySQL0…

RK3568驱动指南|第十二篇 GPIO子系统-第136章 实战:实现动态切换引脚复用功能

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

C语言天花板——指针(进阶3)

篇接上文(http://t.csdnimg.cn/Tl42h)&#xff0c;今天我们来讲一些有趣的关于指针的问题&#x1f6a2;&#x1f6a2;&#x1f6a2; 首先我们来看个代码&#xff1a; int main() {//一维数组int a[] { 1,2,3,4 };//4个元素&#xff0c;每个元素使int类型(4个字节)printf(&qu…

【LeetCode】24. 两两交换链表中的节点(中等)——代码随想录算法训练营Day04

题目链接&#xff1a;24. 两两交换链表中的节点 题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1…

基于JAVA的民宿预定管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色2.2.2 房主角色2.2.3 系统管理员角色 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿4.3 新增民宿评价4.4 查询留言4.5 新增民宿订单 五、免责说明 一、摘要 1.1 项目介绍 基于…

Centos7下升级gcc/g++版本(简单 + 避坑)

在 Centos7 下&#xff0c;使用 yum 安装高版本的 gcc/g 最简单&#xff1a; yum -y install centos-release-scl yum -y install devtoolset-11-gcc devtoolset-11-gcc-c devtoolset-11-binutils需要安装哪个个版本的gcc/g&#xff0c;就把数字替换成对应的版本号。例如上面代…

Velodyne 64E S3 硬件连接和 velodyne_driver ROS驱动安装

在当前的研究工作中&#xff0c;点云语义分割技术的应用显得尤为重要&#xff0c;而我选择了rangenet作为分割算法的核心。尽管课题组拥有一款80线激光雷达&#xff0c;但是在实际测试中&#xff0c;我们发现该算法模型在我们的数据集上的泛化表现并不理想。为了改善这一情况&a…