关于android:安卓集成讯飞语音识别并重新封装

7次阅读

共计 26043 个字符,预计需要花费 66 分钟才能阅读完成。

1. 集成科大讯飞

在讯飞开放平台注册并实现身份认证, 进入控制台页面创立一个利用

按要求实现填写点击创立即可, 接着进入刚刚创立的利用, 在这里能够看到所有的性能详情.

在右侧可看到要害的 APPID APISecret APIKey

我打算应用三个性能: 语音听写 语音合成 语音评测, 所以接下来进入聚合 SDK 下载页下载组合 SDK

抉择好利用、平台、须要的 AI 能力后即可下载

下载后将压缩包解压后的目录后果如下:

其中 sample 目录下是一个官网提供的实例 Demo, 咱们能够将其独自用 AS 关上(报错没关系, 能看清代码即可), 之后的封装都是参照官网 Demo 进行的. 官网传送门: 官网文档

SDK 包阐明:

接着将 SDK 导入咱们的我的项目, 首先将在官网下载的 Android SDK 压缩包中 libs 目录下所有子文件拷贝至 Android 工程的 libs 目录下:

用 AS 关上咱们的我的项目, 右键 msc.jar 增加 Add As Library

接着再咱们我的项目的 main 目录下新建 Jnilibs 目录, 将 libs 目录下的两个文件夹拷贝过去:

如运行有报错, 可再在 Module 级的 build.gradle 中增加:

    ....buildTypes {...}
// 新加: 
// 指定 libs 文件夹地位
    sourceSets {
        main{jniLibs.srcDirs = ['libs']
        }
    }

最初, 初始化讯飞 SDK 即可, 倡议在 Application 下初始化:

@Override
    public void onCreate() {super.onCreate();
        mContext = this.getApplication();
        // 这里实现 SDK 初始化,// 请勿在“=”与 appid 之间增加任何空字符或者本义符
        SpeechUtility.createUtility(mContext, SpeechConstant.APPID + "=" + mContext.getString(R.string.APPID));

    }

倡议将常量字符对立寄存在 res/values/string.xml 下

至此, 集成 SDK 已实现, 接下来进行性能封装:

2. 性能封装

在咱们的包目录下创立一个 utils 包, 官网 Demo 中有两个好用的工具类, 将他们增加到 utils 包下:

在咱们的包目录下新建 iflytek 目录, 用于寄存讯飞相干的类, 首先封装语音听写:

新建接口: RecognizeListener, 新建类: RecognizeSpeechManager

/**
 * 听写回调
 */
public interface RecognizeListener {void onNewResult(String result);

    void onTotalResult(String result,boolean isLast);

    void onError(SpeechError speechError);
}
/**
 * 音频读写转换
 */
public class RecognizeSpeechManager implements RecognizerListener, InitListener {

    private static final String TAG = "RecognizeSpeechManager";

    // 后果回调对象
    private RecognizeListener recognizeListener;

    // 语音听写对象
    private SpeechRecognizer iat;

    private StringBuffer charBufffer = new StringBuffer();

    // 上下文的弱援用, 以便在不应用时回收, 防止内存泄露 (当一个对象仅仅被弱援用指向, 而没有其余强援用指向时, 在下一次 gc 运行时将会被回收)
    private WeakReference<Context> bindContext;

    // 单例
    private static RecognizeSpeechManager instance;

    private RecognizeSpeechManager() {}

    /**
     * 单例办法
     */
    public static RecognizeSpeechManager instance() {if (instance == null) {instance = new RecognizeSpeechManager();
        }
        return instance;
    }

    /**
     * 设置后果回调对象
     */
    public void setRecognizeListener(RecognizeListener recognizeListener) {this.recognizeListener = recognizeListener;}

    /**
     * 初始化
     */
    public void init(Context context) {if (bindContext == null) {bindContext = new WeakReference<Context>(context);
        }
        if (iat == null) {iat = SpeechRecognizer.createRecognizer(bindContext.get(), this);
        }
    }

    @Override
    public void onInit(int code) {if (code != ErrorCode.SUCCESS) {Log.d(TAG, "init error code" + code);
        }
    }

    /**
     * 开始监听
     * ErrorCode.SUCCESS 监听胜利状态码
     */
    public int startRecognize() {setParam();
        return iat.startListening(this);
    }

    /**
     * 勾销听写
     */
    public void cancelRecognize() {iat.cancel();
    }

    /**
     * 进行听写
     */
    public void stopRecognize() {iat.stopListening();
    }

    public void release() {iat.cancel();
        iat.destroy();
        // iat = null;
        bindContext.clear();
        // bindContext = null;
        charBufffer.delete(0, charBufffer.length());
    }

    @Override
    public void onVolumeChanged(int i, byte[] bytes) { }

    @Override
    public void onBeginOfSpeech() {Log.d(TAG, "onBeginOfSpeech");
    }

    @Override
    public void onEndOfSpeech() {Log.d(TAG, "onEndOfSpeech isListening" + iat.isListening());
    }

    @Override
    public void onResult(RecognizerResult results, boolean b) {if (recognizeListener != null) {recognizeListener.onNewResult(printResult(results));
            recognizeListener.onTotalResult(charBufffer.toString(), iat.isListening());
        }
    }


    @Override
    public void onError(SpeechError speechError) {if (recognizeListener != null) {recognizeListener.onError(speechError);
        }
    }

    @Override
    public void onEvent(int i, int i1, int i2, Bundle bundle) {Log.d(TAG, "onEvent type" + i);
    }


    private String printResult(RecognizerResult results) {String text = JsonParser.parseIatResult(results.getResultString());
        Log.d(TAG, "printResult" + text + "isListening" + iat.isListening());
        String sn = null;
        // 读取 json 后果中的 sn 字段
        try {JSONObject resultJson = new JSONObject(results.getResultString());
            sn = resultJson.optString("sn");
        } catch (JSONException e) {e.printStackTrace();
        }
        if (!TextUtils.isEmpty(text)) {charBufffer.append(text);
        }
        return text;
    }

    /**
     * 参数设置
     *
     * @return
     */
    private void setParam() {
        // 清空参数
        iat.setParameter(SpeechConstant.PARAMS, null);

        // 设置听写引擎
        iat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 设置返回后果格局
        iat.setParameter(SpeechConstant.RESULT_TYPE, "json");


        iat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
        iat.setParameter(SpeechConstant.ACCENT, "mandarin");

        // 此处用于设置 dialog 中不显示错误码信息
        //iat.setParameter("view_tips_plain","false");

        // 设置语音前端点: 静音超时工夫,即用户多长时间不谈话则当做超时解决
        iat.setParameter(SpeechConstant.VAD_BOS, "10000");

        // 设置语音后端点: 后端点静音检测时间,即用户进行谈话多长时间内即认为不再输出,主动进行录音
        iat.setParameter(SpeechConstant.VAD_EOS, "10000");

        // 设置标点符号, 设置为 "0" 返回后果无标点, 设置为 "1" 返回后果有标点
        iat.setParameter(SpeechConstant.ASR_PTT, "1");

        // 设置音频保留门路,保留音频格式反对 pcm、wav,设置门路为 sd 卡请留神 WRITE_EXTERNAL_STORAGE 权限
       /* iat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        iat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav");*/
    }

}

应用(其中采纳了 ViewModel 和 butterKnife):

public class HomeFragment extends Fragment implements RecognizeListener {
    //UI 视图的展现和事件蕴含在 Fragment 或 Activity 中

    private Unbinder unbinder;
    private HomeViewModel homeViewModel;
    @BindView(R.id.tvContent)
    TextView tvContent;

    public Context mContext;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {mContext = this.getContext();
        // 构建 ViewModel 实例
        homeViewModel =
                ViewModelProviders.of(this).get(HomeViewModel.class);
        // 创立视图对象
        View root = inflater.inflate(R.layout.fragment_home, container, false);
        // fragment 绑定 butterKnife
        unbinder = ButterKnife.bind(this,root);
        // 让 UI 察看 ViewModel 中数据的变动, 并实时更新 UI
        homeViewModel.getRecognizeText().observe(getViewLifecycleOwner(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {tvContent.setText(s);
            }
        });

        // 初始化讯飞音频读写治理类
        RecognizeSpeechManager.instance().init(mContext);
        RecognizeSpeechManager.instance().setRecognizeListener(this);

        // 返回视图对象
        return root;
    }

    @OnClick({R.id.btStart, R.id.btCancel, R.id.btStop})
    public void onClick(View v) {switch (v.getId()){
            case R.id.btStart:
                RecognizeSpeechManager.instance().startRecognize();
                break;
            case R.id.btCancel:
                RecognizeSpeechManager.instance().cancelRecognize();
                break;
            case R.id.btStop:
                RecognizeSpeechManager.instance().stopRecognize();
                break;
        }
    }

    @Override
    public void onDestroy() {super.onDestroy();
        if(unbinder != null) {unbinder.unbind();// 视图销毁时必须解绑
        }
        RecognizeSpeechManager.instance().release();
    }

    @Override
    public void onNewResult(String result) {homeViewModel.setRecognizeText(homeViewModel.getRecognizeText().getValue() + "最新翻译:" + result + "\n");
    }

    @Override
    public void onTotalResult(String result, boolean isLast) {homeViewModel.setRecognizeText(homeViewModel.getRecognizeText().getValue() + "所有翻译:" + result + "\n");
    }

    @Override
    public void onError(SpeechError speechError) {Toast.makeText(mContext, "出错了" + speechError, Toast.LENGTH_SHORT).show();}
}
public class HomeViewModel extends ViewModel {
    // 数据获取和解决蕴含在 ViewModel 中

    // 辨认后果数据
    private MutableLiveData<String> recognizeText;

    public HomeViewModel() {mText = new MutableLiveData<>();
        recognizeText = new MutableLiveData<>();
        recognizeText.setValue("");
    }

    // get 办法

    public LiveData<String> getRecognizeText () {return recognizeText;}

    // set 办法
    public void setRecognizeText (String recognizeText) {this.recognizeText.setValue(recognizeText);
    }
}

封装语音合成:

​ 新建接口: SynthesizeListener 新建类: SynthesizeSpeechManager

/**
 * 合成回调
 */
public interface SynthesizeListener {void onError(SpeechError speechError);
}
/**
 * 语音合成
 */
public class SynthesizeSpeechManager implements SynthesizerListener, InitListener {

    private static final String TAG = "SynthesizeSpeechManager";

    // 默认发音人
    private String voicer = "xiaoyan";

    // 后果回调对象
    private SynthesizeListener synthesizeListener;

    // 语音合成对象
    private SpeechSynthesizer tts;

    // 上下文的弱援用, 以便在不应用时回收, 防止内存泄露
    private WeakReference<Context> bindContext;

    // 单例
    private static SynthesizeSpeechManager instance;

    private SynthesizeSpeechManager() {}

    /**
     * 单例办法
     */
    public static SynthesizeSpeechManager instance() {if (instance == null) {instance = new SynthesizeSpeechManager();
        }
        return instance;
    }

    /**
     * 设置后果回调对象
     */
    public void setSynthesizeListener(SynthesizeListener synthesizeListener) {this.synthesizeListener = synthesizeListener;}

    /**
     * 初始化
     */
    public void init(Context context) {if (bindContext == null) {bindContext = new WeakReference<Context>(context);
        }
        if (tts == null) {tts = SpeechSynthesizer.createSynthesizer(bindContext.get(), this);
        }
    }

    @Override
    public void onInit(int code) {if (code != ErrorCode.SUCCESS) {Log.d(TAG, "init error code" + code);
        }
    }

    // 接着须要实现自定义接口的三个办法

    /**
     * 开始合成
     */
    public int startSpeak(String texts) {setParam();
        return tts.startSpeaking(texts, this);
    }

    /**
     * 勾销合成
     */
    public void stopSpeak() {tts.stopSpeaking();
    }

    /**
     * 暂停播放
     */
    public void pauseSpeak() {tts.pauseSpeaking();
    }

    /**
     * 持续播放
     */
    public void resumeSpeak() {tts.resumeSpeaking();
    }

    /**
     * 垃圾回收
     */
    public void release() {tts.stopSpeaking();
        tts.destroy();
        // tts = null;
        bindContext.clear();
        // bindContext = null;
    }

    @Override
    public void onSpeakBegin() {Log.d(TAG, "开始播放");
    }

    @Override
    public void onBufferProgress(int percent, int beginPos, int endPos, String info) {Log.d(TAG, "合成进度: percent =" + percent);
    }

    @Override
    public void onSpeakPaused() {Log.d(TAG, "暂停播放");
    }

    @Override
    public void onSpeakResumed() {Log.d(TAG, "持续播放");
    }

    @Override
    public void onSpeakProgress(int percent, int beginPos, int endPos) {Log.e(TAG, "播放进度: percent =" + percent);
    }

    @Override
    public void onCompleted(SpeechError speechError) {Log.d(TAG, "播放实现");
        if (speechError != null) {Log.d(TAG, speechError.getPlainDescription(true));
            synthesizeListener.onError(speechError);
        }
    }

    @Override
    public void onEvent(int eventType, int arg1, int arg2, Bundle bundle) {
        //     以下代码用于获取与云端的会话 id,当业务出错时将会话 id 提供给技术支持人员,可用于查问会话日志,定位出错起因
        if(bundle != null) {Log.d(TAG, "session id =" + bundle.getString(SpeechEvent.KEY_EVENT_SESSION_ID));
            Log.e(TAG, "EVENT_TTS_BUFFER =" + Objects.requireNonNull(bundle.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER)).length);
        }
    }

    /**
     * 参数设置
     */
    private void setParam() {
        // 清空参数
        tts.setParameter(SpeechConstant.PARAMS, null);
        // 设置合成引擎
        tts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 反对实时音频返回,仅在 synthesizeToUri 条件下反对
        tts.setParameter(SpeechConstant.TTS_DATA_NOTIFY, "1");
        //    mTts.setParameter(SpeechConstant.TTS_BUFFER_TIME,"1");
        // 设置在线合成发音人
        tts.setParameter(SpeechConstant.VOICE_NAME, voicer);
        // 设置合成语速
        tts.setParameter(SpeechConstant.SPEED, "50");
        // 设置合成音调
        tts.setParameter(SpeechConstant.PITCH, "50");
        // 设置合成音量
        tts.setParameter(SpeechConstant.VOLUME, "50");
        // 设置播放器音频流类型
        tts.setParameter(SpeechConstant.STREAM_TYPE, "3");
        // 设置播放合成音频打断音乐播放,默认为 true
        tts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "false");

        // 设置音频保留门路,保留音频格式反对 pcm、wav,设置门路为 sd 卡请留神 WRITE_EXTERNAL_STORAGE 权限
        /* mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "pcm");
        mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH,
                getExternalFilesDir("msc").getAbsolutePath() + "/tts.pcm"); */
    }
}

应用:

public class DashboardFragment extends Fragment implements SynthesizeListener {

    private Unbinder unbinder;
    private DashboardViewModel dashboardViewModel;
    @BindView(R.id.etEva)
    EditText editText;

    public Context mContext;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {mContext = this.getContext();
        dashboardViewModel =
                ViewModelProviders.of(this).get(DashboardViewModel.class);
        View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
        unbinder = ButterKnife.bind(this,root);
        dashboardViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {editText.setText(s);
            }
        });

        // 初始化讯飞音频合成治理类
        SynthesizeSpeechManager.instance().init(mContext);
        SynthesizeSpeechManager.instance().setSynthesizeListener(this);

        return root;
    }

    @OnClick({R.id.btParse, R.id.btPaused, R.id.btResumed})
    public void onClick(View v) {switch (v.getId()){
            case R.id.btParse:
                SynthesizeSpeechManager.instance().startSpeak(dashboardViewModel.getText().getValue());
                break;
            case R.id.btPaused:
                SynthesizeSpeechManager.instance().pauseSpeak();
                break;
            case R.id.btResumed:
                SynthesizeSpeechManager.instance().resumeSpeak();
                break;
        }
    }

    @Override
    public void onDestroy() {super.onDestroy();
        if(unbinder != null) {unbinder.unbind();// 视图销毁时必须解绑
        }
        SynthesizeSpeechManager.instance().release();
    }

    @Override
    public void onError(SpeechError speechError) {Toast.makeText(mContext, "出错了" + speechError, Toast.LENGTH_SHORT).show();}

}
public class DashboardViewModel extends ViewModel {

    private MutableLiveData<String> mText;

    public DashboardViewModel() {mText = new MutableLiveData<>();
        mText.setValue("This is dashboard fragment");
    }

    public LiveData<String> getText() {return mText;}

}

封装语音评测:

语音评测绝对简单, 须要将 demo 中 ise.result 包下的内容全副移到咱们的我的项目中(间接复制文件必定是须要改包名的, 怕出错就手动一个一个新建吧)

在咱们的我的项目 iflytek 包下新建 ise 包, 增加上图内容:

在 iflytek 包下, 新建接口: EvaluateListener 新建类: EvaluateSpeechManager

public interface EvaluateListener {void onNewResult(String result);

    void onTotalResult(String result,boolean isLast);

    void onError(SpeechError speechError);
}
/**
 * 语音评测
 */
public class EvaluateSpeechManager implements EvaluatorListener {

    private static final String TAG = "EvaluatSpeechManager";

    private final static String PREFER_NAME = "ise_settings";
    private final static int REQUEST_CODE_SETTINGS = 1;

    // 上下文的弱援用, 以便在不应用时回收, 防止内存泄露
    private WeakReference<Context> bindContext;
    // 后果回调对象
    private EvaluateListener evaluateListener;
    // 语音评测对象
    private SpeechEvaluator ise;
    // 解析后果
    private String lastResult;

    // 单例
    private static EvaluateSpeechManager instance;

    private EvaluateSpeechManager() {}

    /**
     * 单例办法
     */
    public static EvaluateSpeechManager instance() {if (instance == null) {instance = new EvaluateSpeechManager();
        }
        return instance;
    }

    /**
     * 设置后果回调对象
     */
    public void setEvaluateListener(EvaluateListener evaluateListener) {this.evaluateListener = evaluateListener;}

    /**
     * 初始化
     */
    public void init(Context context) {if (bindContext == null) {bindContext = new WeakReference<Context>(context);
        }
        if (ise == null) {ise = SpeechEvaluator.createEvaluator(bindContext.get(), null);
        }
    }

    /**
     * 开始评测
     * @String category 评测类型
     *  - read_syllable : 字
     *  - read_word : 词
     *  - read_sentence : 句
     *  - read_chapter : 诗
     * @String evaText 评测内容
     */
    public int startEvaluate(String category, String evaText) {
        lastResult = null;
        assert ise!=null;
        setParams(category);
        return ise.startEvaluating(evaText, null, this);
    }

    /**
     * 进行评测
     */
    public void stopEvaluate() {ise.stopEvaluating();
    }

    /**
     * 勾销评测
     */
    public void cancelEvaluate() {ise.cancel();
        lastResult = null;
    }

    /**
     * 后果解析
     */
    public Result parseResult() {if(lastResult == null) {return new FinalResult();
        }
        XmlResultParser resultParser = new XmlResultParser();
        return resultParser.parse(lastResult);
    }

    public void release() {ise.cancel();
        ise.destroy();
        // ise = null;
        bindContext.clear();
        // bindContext = null;
    }

    @Override
    public void onVolumeChanged(int volume, byte[] data) {Log.d(TAG, "以后正在谈话,音量大小 =" + volume + "返回音频数据 =" + data.length);
    }

    @Override
    public void onBeginOfSpeech() {Log.d(TAG, "evaluator begin");
    }

    @Override
    public void onEndOfSpeech() {Log.d(TAG, "onEndOfSpeech isListening" + ise.isEvaluating());
    }

    @Override
    public void onResult(EvaluatorResult evaluatorResult, boolean isLast) {Log.d(TAG, "evaluator result :" + isLast);
        StringBuilder builder = new StringBuilder();
        builder.append(evaluatorResult.getResultString()); // evaluatorResult 为原始的 xml 剖析后果, 须要调用解析函数来失去最终后果
        lastResult = builder.toString();
        if(evaluateListener != null) {evaluateListener.onNewResult(builder.toString());
            evaluateListener.onTotalResult(builder.toString(), isLast);
        }
    }

    @Override
    public void onError(SpeechError speechError) {if(evaluateListener != null) {evaluateListener.onError(speechError);
        }
    }

    @Override
    public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {Log.d(TAG, "onEvent type" + eventType);
    }

    private void setParams(String category) {
        // 设置评测语种
        String language = "zh_cn";
        // 设置后果等级(中文仅反对 complete)
        String result_level = "complete";
        // 设置语音前端点: 静音超时工夫,即用户多长时间不谈话则当做超时解决
        String vad_bos = "5000";
        // 设置语音后端点: 后端点静音检测时间,即用户进行谈话多长时间内即认为不再输出,主动进行录音
        String vad_eos = "1800";
        // 语音输入超时工夫,即用户最多能够间断说多长时间;String speech_timeout = "-1";
        // 设置流式版本所需参数 : ent sub plev
        ise.setParameter("ent", "cn_vip");
        ise.setParameter(SpeechConstant.SUBJECT, "ise");
        ise.setParameter("plev", "0");

        // 设置评分百分制 应用 ise_unite  rst  extra_ability 参数
        ise.setParameter("ise_unite", "1");
        ise.setParameter("rst", "entirety");
        ise.setParameter("extra_ability", "syll_phone_err_msg;pitch;multi_dimension");

        ise.setParameter(SpeechConstant.LANGUAGE, language);
        // 设置须要评测的类型
        ise.setParameter(SpeechConstant.ISE_CATEGORY, category);
        ise.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
        ise.setParameter(SpeechConstant.VAD_BOS, vad_bos);
        ise.setParameter(SpeechConstant.VAD_EOS, vad_eos);
        ise.setParameter(SpeechConstant.KEY_SPEECH_TIMEOUT, speech_timeout);
        ise.setParameter(SpeechConstant.RESULT_LEVEL, result_level);
        ise.setParameter(SpeechConstant.AUDIO_FORMAT_AUE, "opus");
        // 设置音频保留门路,保留音频格式反对 pcm、wav,/* ise.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        ise.setParameter(SpeechConstant.ISE_AUDIO_PATH,
                getExternalFilesDir("msc").getAbsolutePath() + "/ise.wav"); */
        // 通过 writeaudio 形式间接写入音频时才须要此设置
        //mIse.setParameter(SpeechConstant.AUDIO_SOURCE,"-1");
    }
}

应用:

public class NotificationsFragment extends Fragment implements EvaluateListener {

    private Unbinder unbinder;
    private NotificationsViewModel notificationsViewModel;

    @BindView(R.id.etEva)
    EditText etEva;

    public Context mContext;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {mContext = this.getContext();
        notificationsViewModel =
                ViewModelProviders.of(this).get(NotificationsViewModel.class);
        View root = inflater.inflate(R.layout.fragment_notifications, container, false);
        // fragment 绑定 butterKnife
        unbinder = ButterKnife.bind(this,root);
        final TextView textView = root.findViewById(R.id.text_notifications);
        notificationsViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {textView.setText(s);
            }
        });
        notificationsViewModel.getEvaluateText().observe(getViewLifecycleOwner(), new Observer<String>() {
            @Override
            public void onChanged(String s) {etEva.setText(s);
            }
        });

        // 初始化讯飞音频评测治理类
        EvaluateSpeechManager.instance().init(mContext);
        EvaluateSpeechManager.instance().setEvaluateListener(this);

        return root;
    }

    @OnClick({R.id.btStart, R.id.btCancel, R.id.btStop, R.id.btParse})
    public void onClick(View v) {switch (v.getId()){
            case R.id.btStart:
                EvaluateSpeechManager.instance().startEvaluate("read_word",notificationsViewModel.getEvaluateText().getValue());
                break;
            case R.id.btCancel:
                EvaluateSpeechManager.instance().cancelEvaluate();
                break;
            case R.id.btStop:
                EvaluateSpeechManager.instance().stopEvaluate();
                break;
            case R.id.btParse:
                notificationsViewModel.setText(EvaluateSpeechManager.instance().parseResult().toString());
        }
    }

    @Override
    public void onDestroy() {super.onDestroy();
        if(unbinder != null) {unbinder.unbind();// 视图销毁时必须解绑
        }
        EvaluateSpeechManager.instance().release();
    }

    @Override
    public void onNewResult(String result) {notificationsViewModel.setText(result);
    }

    @Override
    public void onTotalResult(String result, boolean isLast) {// Toast.makeText(mContext, result, Toast.LENGTH_SHORT).show();}

    @Override
    public void onError(SpeechError speechError) {Toast.makeText(mContext, "出错了" + speechError, Toast.LENGTH_SHORT).show();}
}
public class NotificationsViewModel extends ViewModel {

    private MutableLiveData<String> mText;
    private MutableLiveData<String> evaluateText;

    public NotificationsViewModel() {mText = new MutableLiveData<>();
        evaluateText = new MutableLiveData<>();
        mText.setValue("This is notifications fragment");
        evaluateText.setValue("西瓜");
    }

    public LiveData<String> getText() {return mText;}

    public LiveData<String> getEvaluateText() {return evaluateText;}

    public void setText(String mText) {this.mText.setValue(mText);
    }

    public void setEvaluateText(String evaluateText) {this.evaluateText.setValue(evaluateText);
    }
}

布局文件 res/layout (有应用腾讯的 QMUI, 如果你没有接入 QMUI, 只须要将 QMUIRoundButton 替换为一般 button 即可):

fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.home.HomeFragment">

    <TextView
        android:id="@+id/tvContent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:layout_marginTop="84dp"
        android:padding="20dp"
        app:layout_constraintBottom_toTopOf="@+id/text_home"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:text="开始辨认"
        app:layout_constraintStart_toStartOf="@+id/tvContent"
        app:layout_constraintTop_toBottomOf="@+id/tvContent" />

    <Button
        android:id="@+id/btCancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="32dp"
        android:text="勾销"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvContent" />

    <Button
        android:id="@+id/btStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="进行"
        app:layout_constraintEnd_toStartOf="@+id/btCancel"
        app:layout_constraintStart_toEndOf="@+id/btStart"
        app:layout_constraintTop_toBottomOf="@+id/tvContent" />

    <TextView
        android:id="@+id/text_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:textAlignment="center"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:paddingLeft="16dp"
        android:paddingTop="10dp"
        android:paddingRight="16dp"
        android:paddingBottom="10dp"
        android:text="圆角为短边的一半"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@+id/text_home"
        app:layout_constraintStart_toStartOf="@+id/text_home"
        app:layout_constraintTop_toBottomOf="@+id/text_home"
        app:layout_constraintVertical_bias="0.26"
        app:qmui_isRadiusAdjustBounds="true" />

</androidx.constraintlayout.widget.ConstraintLayout>

fragment_dashboard.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.dashboard.DashboardFragment">

    <EditText
        android:id="@+id/etEva"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:textAlignment="center"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
        android:id="@+id/btParse"
        android:layout_width="93dp"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:paddingLeft="16dp"
        android:paddingTop="10dp"
        android:paddingRight="16dp"
        android:paddingBottom="10dp"
        android:text="开始播放"
        app:layout_constraintStart_toStartOf="@+id/etEva"
        app:layout_constraintTop_toBottomOf="@+id/etEva"
        app:qmui_isRadiusAdjustBounds="true" />

    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
        android:id="@+id/btResumed"
        android:layout_width="93dp"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:paddingLeft="16dp"
        android:paddingTop="10dp"
        android:paddingRight="16dp"
        android:paddingBottom="10dp"
        android:text="持续播放"
        app:layout_constraintEnd_toEndOf="@+id/etEva"
        app:layout_constraintTop_toBottomOf="@+id/etEva"
        app:qmui_isRadiusAdjustBounds="true" />

    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
        android:id="@+id/btPaused"
        android:layout_width="93dp"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:paddingLeft="16dp"
        android:paddingTop="10dp"
        android:paddingRight="16dp"
        android:paddingBottom="10dp"
        android:text="暂停播放"
        app:layout_constraintEnd_toStartOf="@+id/btResumed"
        app:layout_constraintHorizontal_bias="0.52"
        app:layout_constraintStart_toEndOf="@+id/btParse"
        app:layout_constraintTop_toBottomOf="@+id/etEva"
        app:qmui_isRadiusAdjustBounds="true" />

</androidx.constraintlayout.widget.ConstraintLayout>

fragment_notifications.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.notifications.NotificationsFragment">

    <EditText
        android:id="@+id/etEva"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="96dp"
        android:layout_marginEnd="8dp"
        android:textAlignment="center"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
        android:id="@+id/btStart"
        android:layout_width="93dp"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:layout_marginTop="20dp"
        android:text="开始评测"
        app:layout_constraintEnd_toEndOf="@+id/btParse"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/btParse"
        app:layout_constraintTop_toBottomOf="@+id/btParse"
        app:qmui_isRadiusAdjustBounds="true" />

    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
        android:id="@+id/btStop"
        android:layout_width="93dp"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:layout_marginTop="20dp"
        android:text="进行评测"
        app:layout_constraintEnd_toEndOf="@+id/btCancel"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/btCancel"
        app:layout_constraintTop_toBottomOf="@+id/btCancel"
        app:qmui_isRadiusAdjustBounds="true" />

    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
        android:id="@+id/btCancel"
        android:layout_width="93dp"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:layout_marginEnd="52dp"
        android:text="勾销评测"
        app:layout_constraintEnd_toEndOf="@+id/etEva"
        app:layout_constraintTop_toBottomOf="@+id/etEva"
        app:qmui_isRadiusAdjustBounds="true" />

    <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
        android:id="@+id/btParse"
        android:layout_width="93dp"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:layout_marginStart="56dp"
        android:text="后果解析"
        app:layout_constraintStart_toStartOf="@+id/etEva"
        app:layout_constraintTop_toBottomOf="@+id/etEva"
        app:qmui_isRadiusAdjustBounds="true" />

    <TextView
        android:id="@+id/text_notifications"
        android:layout_width="364dp"
        android:layout_height="285dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:textAlignment="center"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btStart"
        app:layout_constraintVertical_bias="0.060000002" />
</androidx.constraintlayout.widget.ConstraintLayout>
正文完
 0