安卓集成讯飞语音识别并重新封装

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>

以上是 安卓集成讯飞语音识别并重新封装 的全部内容, 来源链接: utcz.com/z/267543.html

回到顶部