修复 Android TV 上控件的焦点

问题描述 投票:0回答:3

我花了很多时间来理解为什么它不起作用,但仍然不知道如何修复它。 我找到了这个解决方案 https://github.com/flutter/flutter/issues/49783 ,但它对我没有帮助。 我的问题是按下 RC 上的选择按钮时开关没有改变(我正在使用模拟器)。 重现步骤:

  • 启动应用程序
  • 在第一个文本控件中输入一些文本(在我的例子中,我需要按下和向上按钮来显示键盘,这是另一个错误,修复起来可能很酷)
  • 按返回 RC 按钮(顺便说一句,如果你知道如何在这里使用提交,请告诉我)隐藏键盘
  • 按下 RC 按钮 - 焦点将切换
  • 然后尝试按选择按钮(第一次有效)
  • 然后返回到文本字段并按下返回然后按下 RC 按钮
  • 现在 switch 在按下 Select 时不起作用
  • 按下选择按钮仍然不起作用
  • 向上按 - 选择切换开关和按钮再次工作! 如果你去文本字段,那么它会重复

我的示例代码:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Shortcuts(
      shortcuts: <LogicalKeySet, Intent>{
        LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
      },
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool switchValue = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Column(
          // mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.symmetric(vertical: 10),
              child: Focus(
                canRequestFocus: false,
                onKey: (FocusNode node, RawKeyEvent event) {
                  if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
                    FocusManager.instance.primaryFocus!
                        .focusInDirection(TraversalDirection.left);
                  } else if (event.logicalKey ==
                      LogicalKeyboardKey.arrowRight) {
                    FocusManager.instance.primaryFocus!
                        .focusInDirection(TraversalDirection.right);
                  } else if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
                    FocusManager.instance.primaryFocus!
                        .focusInDirection(TraversalDirection.up);
                  } else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
                    FocusManager.instance.primaryFocus!
                        .focusInDirection(TraversalDirection.down);
                  }
                  return KeyEventResult.handled;
                },
                child: TextField(
                  autofocus: true,
                ),
              ),
            ),
            Switch(
              value: switchValue,
              onChanged: (value) {
                setState(() {
                  switchValue = value;
                });
              },
            ),
            TextButton(
              onPressed: () => print('Button pressed'),
              style: TextButton.styleFrom(
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(18.0),
                ),
              ),
              child: const Text('Test'),
            ),
          ],
        ),
      ),
    );
  }
}

这是我的 AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.testfocus.test_focus">
<application
     android:label="test_focus"
     android:name="${applicationName}"
     android:icon="@mipmap/ic_launcher">
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:launchMode="singleTop"
        android:theme="@style/LaunchTheme"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:windowSoftInputMode="adjustResize">
        <!-- Specifies an Android theme to apply to this Activity as soon as
             the Android process has started. This theme is visible to the user
             while the Flutter UI initializes. After that, this theme continues
             to determine the Window background behind the Flutter UI. -->
        <meta-data
          android:name="io.flutter.embedding.android.NormalTheme"
          android:resource="@style/NormalTheme"
          />
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <!-- Don't delete the meta-data below.
         This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
    <meta-data
        android:name="flutterEmbedding"
        android:value="2" />
</application>
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
android flutter focus textfield television
3个回答
0
投票

我在使用 flutter 开发电视应用程序时遇到了类似的问题。

因为文本字段有它自己的焦点,我找到了一个解决方法。

我禁用了文本字段的输入并出现在屏幕键盘上以在文本字段中输入值。

例如这个:

根据按钮按下字符在文本字段上分配值这将是一个很好的解决方法并且更加用户友好。 在使用 Flutter 开发电视应用程序时尽量减少用户输入。


0
投票

我在为 android 电视制作 IPTV 应用程序时也遇到了这个问题,每当焦点转移到文本字段之后我无法使用电视遥控器的 Enter 按钮但我注意到如果按两次向下按钮和一次向上按钮计时 Enter 按钮再次开始工作 原因仍然未知,这是一个悬而未决的问题,唯一的解决方案是通过创建屏幕键盘和点击时的映射值来填充文本字段 希望这有帮助


0
投票

我不确定我是否迟到了,但这是 Android TV 方向键导航的固定解决方法。

请注意,在文本字段中,我们需要读取 keyUp 事件。这是因为 Android TV 上的键盘似乎读取了 keyUp 事件。因此,如果按下 keyDown,将出现键盘,如果释放该键,将触发 keyUp 事件,该事件将从键盘获取输入。这就是为什么所有操作都在 keyUp 上完成的原因。 (我花了一周时间为我的应用解决这个问题)

如果文本字段已经有焦点,我们需要添加一个包装器,使文本字段成为选择键的焦点。 (这只是一种解决方法)

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Shortcuts(
      shortcuts: <LogicalKeySet, Intent>{
        LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
      },
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool switchValue = false;
  final FocusScopeNode node = FocusScopeNode();
  final textFocus = FocusNode();
  final textWrapper = FocusNode();
  final switchFocus = FocusNode();
  final btnNode = FocusNode();

  @override
  void initState() {
    textFocus.addListener(_listener);
    btnNode.addListener(_listener);
    textWrapper.addListener(_listener);
    switchFocus.addListener(_listener);
    super.initState();
  }

  _listener() {
    if (textFocus.hasFocus ||
        textWrapper.hasFocus ||
        switchFocus.hasFocus ||
        btnNode.hasFocus) {
      setState(() {});
    }
  }

  @override
  void dispose() {
    textFocus.removeListener(_listener);
    btnNode.removeListener(_listener);
    textWrapper.removeListener(_listener);
    switchFocus.removeListener(_listener);
    node.dispose();
    textFocus.dispose();
    switchFocus.dispose();
    btnNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Demo Home Page'),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 10),
        child: FocusScope(
          autofocus: true,
          onFocusChange: (val) {
            if (val) textFocus.requestFocus();
          },
          onKey: (FocusNode node, RawKeyEvent event) {
            if (event is RawKeyUpEvent &&
                event.data is RawKeyEventDataAndroid) {
              RawKeyUpEvent rawKeyDownEvent = event;
              RawKeyEventDataAndroid rawKeyEventDataAndroid =
                  rawKeyDownEvent.data as RawKeyEventDataAndroid;

              if (rawKeyEventDataAndroid.keyCode == KEY_CENTER) {
                if (textFocus.hasFocus) {
                  textFocus.unfocus();
                  Future.delayed(Duration.zero).then((value) {
                    textFocus.requestFocus();
                  });
                } else if (textWrapper.hasFocus) {
                  textFocus.requestFocus();
                }
              }

              if (rawKeyEventDataAndroid.keyCode == KEY_DOWN) {
                if (textWrapper.hasFocus) {
                  switchFocus.requestFocus();
                } else if (switchFocus.hasFocus) {
                  btnNode.requestFocus();
                } else {
                  textFocus.requestFocus();
                }
              }

              if (rawKeyEventDataAndroid.keyCode == KEY_UP) {
                if (btnNode.hasFocus) {
                  switchFocus.requestFocus();
                } else if (switchFocus.hasFocus) {
                  textFocus.requestFocus();
                } else {
                  btnNode.requestFocus();
                }
              }
            }
            return KeyEventResult.handled;
          },
          child: Column(
            children: [
              RawKeyboardListener(
                focusNode: textWrapper,
                child: TextField(
                  focusNode: textFocus,
                  autofocus: true,
                ),
              ),
              Shortcuts(
                shortcuts: <LogicalKeySet, Intent>{
                  LogicalKeySet(LogicalKeyboardKey.select):
                      const ActivateIntent(),
                  LogicalKeySet(LogicalKeyboardKey.enter):
                      const ActivateIntent(),
                },
                child: Switch(
                  value: switchValue,
                  focusNode: switchFocus,
                  onChanged: (value) {
                    setState(() {
                      switchValue = value;
                    });
                  },
                ),
              ),
              Shortcuts(
                shortcuts: <LogicalKeySet, Intent>{
                  LogicalKeySet(LogicalKeyboardKey.select):
                      const ActivateIntent(),
                  LogicalKeySet(LogicalKeyboardKey.enter):
                      const ActivateIntent(),
                },
                child: TextButton(
                  onPressed: () => print('Button pressed'),
                  focusNode: btnNode,
                  style: TextButton.styleFrom(
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(18.0),
                    ),
                  ),
                  child: const Text('Test'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

const int KEY_UP = 19;
const int KEY_DOWN = 20;
const int KEY_LEFT = 21;
const int KEY_RIGHT = 22;
const int KEY_CENTER = 23;
const int KEY_BACK = 4;
© www.soinside.com 2019 - 2024. All rights reserved.