91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android如何使用Flutter實現錄音插件

發布時間:2022-08-02 09:23:31 來源:億速云 閱讀:278 作者:iii 欄目:開發技術

本篇內容主要講解“Android如何使用Flutter實現錄音插件”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Android如何使用Flutter實現錄音插件”吧!

安卓部分

手動注冊

Flutter 官方的做法,就是自動注冊插件,

很方便

手動注冊,體現本文的不同

插件是 AudioRecorderPlugin

class MainActivity: FlutterActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        flutterEngine!!.plugins.add(AudioRecorderPlugin())
    }
}

Android和Dart的通訊

主要是消息回調

下文依次是,

  • 開始錄音

  • 結束錄音

  • 正在錄音

  • 是否有錄音權限

注意,這里的錄音權限包含兩個,麥克風的權限,和存儲權限

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
  switch (call.method) {
    case "start":
      Log.d(LOG_TAG, "Start");
      Log.d(LOG_TAG, "11111____");
      String path = call.argument("path");
      mExtension = call.argument("extension");
      startTime = Calendar.getInstance().getTime();
      if (path != null) {
        mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + path;
      } else {
        Log.d(LOG_TAG, "11111____222");
        String fileName = String.valueOf(startTime.getTime());
        mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + fileName + mExtension;
      }
      Log.d(LOG_TAG, mFilePath);
      startRecording();
      isRecording = true;
      result.success(null);
      break;
    case "stop":
      Log.d(LOG_TAG, "Stop");
      stopRecording();
      long duration = Calendar.getInstance().getTime().getTime() - startTime.getTime();
      Log.d(LOG_TAG, "Duration : " + String.valueOf(duration));
      isRecording = false;
      HashMap<String, Object> recordingResult = new HashMap<>();
      recordingResult.put("duration", duration);
      recordingResult.put("path", mFilePath);
      recordingResult.put("audioOutputFormat", mExtension);
      result.success(recordingResult);
      break;
    case "isRecording":
      Log.d(LOG_TAG, "Get isRecording");
      result.success(isRecording);
      break;
    case "hasPermissions":
      Log.d(LOG_TAG, "Get hasPermissions");
      Context context = _flutterBinding.getApplicationContext();
      PackageManager pm = context.getPackageManager();
      int hasStoragePerm = pm.checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, context.getPackageName());
      int hasRecordPerm = pm.checkPermission(Manifest.permission.RECORD_AUDIO, context.getPackageName());
      boolean hasPermissions = hasStoragePerm == PackageManager.PERMISSION_GRANTED && hasRecordPerm == PackageManager.PERMISSION_GRANTED;
      result.success(hasPermissions);
      break;
    default:
      result.notImplemented();
      break;
  }
}

安卓錄音

使用 wav 的封裝格式,用 AudioRecord;

其他封裝格式,用 MediaRecorder

上面兩個播放器,有開始錄音和結束錄音功能;

暫停錄音和恢復錄音,則多次開始和結束,再把文件拼接在一起

Android如何使用Flutter實現錄音插件

Dart module部分

建立 MethodChannel, 異步調用上面的原生功能

class AudioRecorder {
  static const MethodChannel _channel = const MethodChannel('audio_recorder');
  static LocalFileSystem fs = LocalFileSystem();
  static Future start(String path, AudioOutputFormat audioOutputFormat) async {
    String extension;
    if (path != null) {
      if (audioOutputFormat != null) {
        if (_convertStringInAudioOutputFormat(p.extension(path)) !=
            audioOutputFormat) {
          extension = _convertAudioOutputFormatInString(audioOutputFormat);
          path += extension;
        } else {
          extension = p.extension(path);
        }
      } else {
        if (_isAudioOutputFormat(p.extension(path))) {
          extension = p.extension(path);
        } else {
          extension = ".m4a"; // default value
          path += extension;
        }
      }
      File file = fs.file(path);
      if (await file.exists()) {
        throw new Exception("A file already exists at the path :" + path);
      } else if (!await file.parent.exists()) {
        throw new Exception("The specified parent directory does not exist");
      }
    } else {
      extension = ".m4a"; // default value
    }
    return _channel
        .invokeMethod('start', {"path": path, "extension": extension});
  }
  static Future<Recording?> stop() async {
    // 把原生帶出來的信息,放入字典中
    Map<String, dynamic> response =
        Map.from(await _channel.invokeMethod('stop'));
    if (response != null) {
      int duration = response['duration'];
      String fmt = response['audioOutputFormat'];
      AudioOutputFormat? outputFmt = _convertStringInAudioOutputFormat(fmt);
      if (fmt != null && outputFmt != null) {
        Recording recording = new Recording(
            new Duration(milliseconds: duration),
            response['path'],
            outputFmt,
            response['audioOutputFormat']);
        return recording;
      }
    } else {
      return null;
    }
  }

iOS部分

手動注冊插件

這里的插件名, 為 SwiftAudioRecorderPlugin

public class SwiftAudioRecorderPlugin: NSObject, FlutterPlugin {
    var isRecording = false
    var hasPermissions = false
    var mExtension = ""
    var mPath = ""
    var startTime: Date!
    var audioRecorder: AVAudioRecorder?
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "audio_recorder", binaryMessenger: registrar.messenger())
    let instance = SwiftAudioRecorderPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }
  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
        case "start":
            print("start")
            let dic = call.arguments as! [String : Any]
            mExtension = dic["extension"] as? String ?? ""
            mPath = dic["path"] as? String ?? ""
            startTime = Date()
            let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            if mPath == "" {
                mPath = documentsPath + "/" + String(Int(startTime.timeIntervalSince1970)) + ".m4a"
            }
            else{
                mPath = documentsPath + "/" + mPath
            }
            print("path: " + mPath)
            let settings = [
                AVFormatIDKey: getOutputFormatFromString(mExtension),
                AVSampleRateKey: 12000,
                AVNumberOfChannelsKey: 1,
                AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
            ]
            do {
                try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
                try AVAudioSession.sharedInstance().setActive(true)

                let recorder = try AVAudioRecorder(url: URL(string: mPath)!, settings: settings)
                recorder.delegate = self
                recorder.record()
                audioRecorder = recorder
            } catch {
                print("fail")
                result(FlutterError(code: "", message: "Failed to record", details: nil))
            }
            isRecording = true
            result(nil)
        case "pause":
            audioRecorder?.pause()
            result(nil)
        case "resume":
            audioRecorder?.record()
            result(nil)
        case "stop":
            print("stop")
            audioRecorder?.stop()
            audioRecorder = nil
            let duration = Int(Date().timeIntervalSince(startTime as Date) * 1000)
            isRecording = false
            var recordingResult = [String : Any]()
            recordingResult["duration"] = duration
            recordingResult["path"] = mPath
            recordingResult["audioOutputFormat"] = mExtension
            result(recordingResult)
        case "isRecording":
            print("isRecording")
            result(isRecording)
        case "hasPermissions":
            print("hasPermissions")
        switch AVAudioSession.sharedInstance().recordPermission{
            case AVAudioSession.RecordPermission.granted:
                print("granted")
                hasPermissions = true
            case AVAudioSession.RecordPermission.denied:
                print("denied")
                hasPermissions = false
            case AVAudioSession.RecordPermission.undetermined:
                print("undetermined")
                AVAudioSession.sharedInstance().requestRecordPermission() { [unowned self] allowed in
                    DispatchQueue.main.async {
                        if allowed {
                            self.hasPermissions = true
                        } else {
                            self.hasPermissions = false
                        }
                    }
                }
            default:()
            }
            result(hasPermissions)
        default:
            result(FlutterMethodNotImplemented)
        }
      }
    }

iOS插件

邏輯與安卓插件類似,

因為 iOS 的 AVAudioRecorderpauseresume 操作,支持友好,

所以增添了暫停和恢復錄音功能

iOS 端的權限比安卓權限,少一個

僅需要錄音麥克風權限

public class SwiftAudioRecorderPlugin: NSObject, FlutterPlugin {
    var isRecording = false
    var hasPermissions = false
    var mExtension = ""
    var mPath = ""
    var startTime: Date!
    var audioRecorder: AVAudioRecorder?
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "audio_recorder", binaryMessenger: registrar.messenger())
    let instance = SwiftAudioRecorderPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }
  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
        case "start":
            print("start")
            let dic = call.arguments as! [String : Any]
            mExtension = dic["extension"] as? String ?? ""
            mPath = dic["path"] as? String ?? ""
            startTime = Date()
            let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            if mPath == "" {
                mPath = documentsPath + "/" + String(Int(startTime.timeIntervalSince1970)) + ".m4a"
            }
            else{
                mPath = documentsPath + "/" + mPath
            }
            print("path: " + mPath)
            let settings = [
                AVFormatIDKey: getOutputFormatFromString(mExtension),
                AVSampleRateKey: 12000,
                AVNumberOfChannelsKey: 1,
                AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
            ]
            do {
                try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
                try AVAudioSession.sharedInstance().setActive(true)
                let recorder = try AVAudioRecorder(url: URL(string: mPath)!, settings: settings)
                recorder.delegate = self
                recorder.record()
                audioRecorder = recorder
            } catch {
                print("fail")
                result(FlutterError(code: "", message: "Failed to record", details: nil))
            }
            isRecording = true
            result(nil)
        case "pause":
            audioRecorder?.pause()
            result(nil)
        case "resume":
            audioRecorder?.record()
            result(nil)
        case "stop":
            print("stop")
            audioRecorder?.stop()
            audioRecorder = nil
            let duration = Int(Date().timeIntervalSince(startTime as Date) * 1000)
            isRecording = false
            var recordingResult = [String : Any]()
            recordingResult["duration"] = duration
            recordingResult["path"] = mPath
            recordingResult["audioOutputFormat"] = mExtension
            result(recordingResult)
        case "isRecording":
            print("isRecording")
            result(isRecording)
        case "hasPermissions":
            print("hasPermissions")
        switch AVAudioSession.sharedInstance().recordPermission{
            case AVAudioSession.RecordPermission.granted:
                print("granted")
                hasPermissions = true
            case AVAudioSession.RecordPermission.denied:
                print("denied")
                hasPermissions = false
            case AVAudioSession.RecordPermission.undetermined:
                print("undetermined")
                AVAudioSession.sharedInstance().requestRecordPermission() { [unowned self] allowed in
                    DispatchQueue.main.async {
                        if allowed {
                            self.hasPermissions = true
                        } else {
                            self.hasPermissions = false
                        }
                    }
                }
            default:()
            }
            result(hasPermissions)
        default:
            result(FlutterMethodNotImplemented)
        }
      }
    }

Dart調用部分

通過判斷平臺,Platform.isIOS,

給 iOS 設備,增加完善的功能

@override
Widget build(BuildContext context) {
  final VoidCallback tapFirst;
  if (Platform.isAndroid && name == kEnd) {
    tapFirst = _audioEnd;
  } else {
    tapFirst = _audioGoOn;
  }
  List<Widget> views = [
    ElevatedButton(
      child: Text(
        name,
        style: Theme.of(context).textTheme.headline4,
      ),
      onPressed: tapFirst,
    )
  ];
  if (Platform.isIOS && name != kStarted) {
    views.add(SizedBox(height: 80));
    views.add(ElevatedButton(
      child: Text(
        kEnd,
        style: Theme.of(context).textTheme.headline4,
      ),
      onPressed: _audioEnd,
    ));
  }
  return Scaffold(
    appBar: AppBar(
      // Here we take the value from the MyHomePage object that was created by
      // the App.build method, and use it to set our appbar title.
      title: Text(widget.title),
    ),
    body: Center(
      // Center is a layout widget. It takes a single child and positions it
      // in the middle of the parent.
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: views,
      ),
    ), // This trailing comma makes auto-formatting nicer for build methods.
  );
}

到此,相信大家對“Android如何使用Flutter實現錄音插件”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

洛浦县| 新安县| 五河县| 勃利县| 新津县| 旬邑县| 清镇市| 吉安县| 浦东新区| 新平| 清新县| 宝山区| 华坪县| 广饶县| 花莲市| 团风县| 葵青区| 镇康县| 绥滨县| 高唐县| 巧家县| 江达县| 屏东县| 嵊泗县| 黎川县| 瑞金市| 宜兴市| 罗源县| 芮城县| 齐河县| 松江区| 宝山区| 历史| 当雄县| 秦安县| 囊谦县| 拜城县| 比如县| 安远县| 工布江达县| 长沙县|