Flutter插件开发之物理音量按键监听

共 7848字,需浏览 16分钟

 ·

2021-03-09 09:51

Flutter 2.0 来啦,我还是很兴奋的,因为它让我感受到了 Google 对做大做强 Flutter 的决心。但今天想记录的是如何实现物理音量按键监听的 Flutter 插件,之前介绍过 Flutter 与原生的通信。这里在加深一下两者通信方式的印象。另外: 对 Flutter 2 感兴趣的可以去官网围观。

当时写这个插件就是为了实现,点击物理音量键 + - 实现小说翻页的功能。

Android 端物理音量按键的监听

这里思路简单,我用一个变量 interceptKeyDownEnabled 控制当前是否响应按键监听事件,重写 Android 的 onKeyDown 方法,当检测到点击物理音量按键时,通过 EventChannel.EventSink 把按键事件传到 Flutter 端,以此实现监听通信。

变量定义

注意这里 CHANNELENABLEPATH,CHANNELEVENTPATH 定义的值必须和 Flutter 端定义的通道一致。

    //用一个变量控制是否启用监听音量按键    private var interceptKeyDownEnabled: Boolean = false;
private val CHANNEL_ENABLE_PATH: String = "volume_channel_enable_path_method"; private val CHANNEL_EVENT_PATH: String = "volume_channel_path_event";
//监听按键的Event var eventSink: EventChannel.EventSink? = null

初始化

重写 configureFlutterEngine 方法,进行通道初始化。

   override fun configureFlutterEngine(flutterEngine: FlutterEngine) {        super.configureFlutterEngine(flutterEngine)        GeneratedPluginRegistrant.registerWith(flutterEngine);
MethodChannel(flutterEngine.dartExecutor, CHANNEL_ENABLE_PATH).setMethodCallHandler { call, result -> when (call.method) { "setVolumeListenEnable" -> { val map = call.arguments as HashMap<*, *> //控制是否响应监听 interceptKeyDownEnabled = map["state"] as Boolean; // 如果成功返回true,失败返回false result.success(true) // result.error("ERROR_CODE", "error message", null) } else -> { // 未实现 result.notImplemented() } } };

EventChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL_EVENT_PATH).setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { eventSink = events!! }
override fun onCancel(arguments: Any?) { eventSink=null; } })
}

重写 onKeyDown 事件方法

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if(interceptKeyDownEnabled){ // keydown 拦截事件 when (keyCode) { KeyEvent.KEYCODE_VOLUME_DOWN -> { eventSink?.success("volumeDown") return true } KeyEvent.KEYCODE_VOLUME_UP -> { eventSink?.success("volumeUp") return true } } //进行 按键事件拦截事件 return true; }

return super.onKeyDown(keyCode, event) }

Android 端的编码至此完结。

IOS 端音量按键监听

IOS 端我找了好久,没有发现类似 Android 重写按键监听事件的方法。所以我换种思路,通过监听音量大小的变化来判断用户点击了音量+ 还是 -的操作。

定义变量

    //监听当前音量变化    var currentVolume:Float?    var eventSink: FlutterEventSink?    //是否监听    var listenEnable=false;

初始化

在 AppDelegate.swift 的 application 中进行初始化。

    let controller : FlutterViewController  = window?.rootViewController as! FlutterViewController        let enableChannel = FlutterMethodChannel.init(name: "volume_channel_enable_path_method",                                                  binaryMessenger: controller.binaryMessenger);       let eventChannel = FlutterEventChannel(name: "volume_channel_path_event", binaryMessenger: controller.binaryMessenger)    eventChannel.setStreamHandler(self);            enableChannel.setMethodCallHandler({        (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in        // Handle battery messages.        if call.method == "setVolumeListenEnable" {            self.setVolumeListenEnable(call: call,result: result)        }        else {            result(FlutterMethodNotImplemented);        }    })


// eventChannel.setStreamHandler(self);extension AppDelegate: FlutterStreamHandler {             public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {                self.eventSink=events;        return nil    }        public func onCancel(withArguments arguments: Any?) -> FlutterError? {        return nil    }}


然后紧接着在application中调用 initAndLoadVolum 先把 当前音量传到 Flutter 端。

    //初始化,首次获取音量    initAndLoadVolum();
    //注册监听    func initAndLoadVolum() {        do{            try AVAudioSession.sharedInstance().setActive(true)        }catch let error as NSError{            print("\(error)")        }        currentVolume = AVAudioSession.sharedInstance().outputVolume        //初次启动的时候把当前音量传递至Flutter 端进行赋值        self.eventSink?(currentVolume);        NotificationCenter.default.addObserver(self, selector: #selector(self.changeVolumSlider), name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil)        UIApplication.shared.beginReceivingRemoteControlEvents()            }

IOS 音量变化监听

    @objc func changeVolumSlider(notifi:NSNotification) {            if let volum:Float = notifi.userInfo?["AVSystemController_AudioVolumeNotificationParameter"] as! Float?{                //返回                if !self.listenEnable {                    return;                }                self.eventSink?(volum);            }        }                deinit {            NotificationCenter.default.removeObserver(self)            UIApplication.shared.endReceivingRemoteControlEvents()        }            func setVolumeListenEnable(call: FlutterMethodCall,result: @escaping FlutterResult)  {        let dic: Dictionary<String, Any> = call.arguments as! Dictionary<String, Any>        self.listenEnable=dic["state"] as! Bool;               }

Flutter 端实现

通过定义 MethodChannel 和 EventChannel 通道分别调用,监听 Android,IOS 端的方法,和传回来的值。

通道定义

const String VolumeChanelEnableName = "volume_channel_enable_path_method";
const String VolumeChanelEventName = "volume_channel_path_event";

通道初始化

 static const MethodChannel _volume_channel =      const MethodChannel(VolumeChanelEnableName);  static const EventChannel _volume_event_channel =      const EventChannel(VolumeChanelEventName);

调用方法,监听方法的实现

调用方法没啥说的,监听方法,因为 Android 端和 IOS 端我传过来的值是不同类型的所以区分一下平台。

  //音量控制监听相关  static void setVolumeListenEnable(bool isStop) async {    await _volume_channel        .invokeMethod("setVolumeListenEnable", {"state": isStop});  }
  //监听,此处因为Ios没有直接监听音量按键的点击响应事件,所以我改为直接监听Ios变化情况,  //但IOS除了物理按键,也可以从控制中心改变音量,  //所以如果要在实际中运用,可以设置点时间间隔控制。  static void onVolumeListen(ValueChanged<String> onKeyDownEvent) {    double _currentValue = -100.0;    _volume_event_channel.receiveBroadcastStream().listen((dynamic msgData) {      print("volume ---->" + msgData.toString());
if (Platform.isAndroid) { onKeyDownEvent(msgData); return; }
if (Platform.isIOS) { if (_currentValue == -100.0) { //接收到的初始化值 _currentValue = msgData; return; } if (msgData > _currentValue) { onKeyDownEvent("volumeUp"); } else if (msgData < _currentValue) { onKeyDownEvent("volumeDown"); } else if (msgData == 1.0) { onKeyDownEvent("volumeUp"); } else if (msgData == 0.0) { onKeyDownEvent("volumeDown"); } _currentValue = msgData; } }, onError: (Object error) { print("volume:error---->" + error.toString()); }); }

插件的运用

调用 setVolumeListenEnable 方法启用或者关闭监听,onVolumeListen 注册监听。

  @override  void initState() {    // TODO: implement initState    super.initState();    VolumePlugin.setVolumeListenEnable(true);    VolumePlugin.onVolumeListen((value) {      if (mounted) {        setState(() {          _content = value;        });      }    });  }
  @override  void dispose() {    // TODO: implement dispose    super.dispose();    VolumePlugin.setVolumeListenEnable(false);  }

完整的代码和效果图我就不贴了,这篇看下来,我觉得你对 Flutter 和原生端通信会更加深刻。


浏览 237
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报