草庐IT

Flutter TextField 交互实例 —— 新手礼包

IAM17前端 2023-04-13 原文

大家好,我是 17。

新手礼包一共 3 篇文章,每篇都是描述尽量详细,实例讲解,包会!

本篇包含所有常见 TextField 交互示例。

设置初始值

在上一篇 Flutter TextField UI 实例 中第一个示例中已经给出了全部代码,并准备好了 controller。

我们梳理一下设置初始值需要的步骤,需要两步完成设置初始值。

  1. 初始化 controller var controller = TextEditingController(text: "IAM17");
  2. 把 controller 赋值给 TextField
TextField(
     controller: controller,
 );

完成这两步后,在 TextField 中自就会有文本 “IAM17”。

获取和修改 TextField 内容

有两个办法可以拿到内容。

  1. 监听 onChange
 TextField(
     controller: controller,
     onChanged: (value) {
       print(value);
     },
 );

通过 controller 修改文本不会触发 onChange

  1. 用 controller 拿到 value。
Column(mainAxisSize: MainAxisSize.min, children: [
   ElevatedButton(
       onPressed: () {
         print(controller.text);
       },
       child: Text('获取内容')),
   TextField(
     controller: controller,
   )
]);

或者当文本发生改变时获取文本

 
  void initState() {
    controller.addListener(() {
      print(controller.text);
    });
    super.initState();
 }
 
  1. 用 controller 可以随时修改 TextField 内容

比如可以随时清空 TextField。

Column(mainAxisSize: MainAxisSize.min, children: [
      ElevatedButton(
          onPressed: () {
            controller.text='';
          },
          child: Text('清空')),
      TextField(
        controller: controller,
      )
 ]);

清空内容还可以用 controller.clear()

注意:controller 必须在 disapose 方法中销毁。

 
  void dispose() {
    controller.dispose();
    super.dispose();
  }

控制 TextField Focus

自动获得焦点

TextField(
      autofocus: true,
 );

手动让一个 TextField 获得或失去焦点

手动获得焦点分四步

  1. 创建 focusNode。
  2. 把 focusNode 赋值给 TextField。
  3. 手动触发 focusNode 的 previousFocus、unfocus 方法获得和失去焦点。
  4. 在 dispose 中销毁 focusNode。
class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  
  State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
  FocusNode focusNode = FocusNode(); //第一步
  
  void dispose() {
    focusNode.dispose(); //第四步
    super.dispose();
  }

  
  Widget build(BuildContext context) {

    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        TextField(
          focusNode: focusNode, // 第二步
        )
        ElevatedButton(
            onPressed: () {  // 第三步
              focusNode.hasFocus
                  ? focusNode.unfocus()
                  : focusNode.requestFocus();
            },
            child: Text('变换焦点')),        
      ],
    );
  }
}

如果是在手机上执行代码,会看到 TextField 获取焦点的时候会弹出软键盘,失去焦点的时候,软键盘收起。

监听获取焦点和失去焦点

因为 focusNode 混入了 ChangeNotifier,所以可以直接监听他的状态变化。

class _MyWidgetState extends State<MyWidget> {
  var focusNode = FocusNode();
  
  void initState() {
    focusNode.addListener(() {
      print(focusNode.hasFocus);
    });
    super.initState();
  }

  
  void dispose() {
    focusNode.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return TextField(
      focusNode: focusNode,
    );
  }
}

控制焦点在不同 TextField 间切换

两个 TextField,第一个自动获得焦点,点击按钮,焦点从第一个 TextField 切换到第二个,再次点击,焦点从第二个 TextField 回到第一个。

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});
  
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  var focusNode1 = FocusNode();
  var focusNode2 = FocusNode();

  
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        TextField(
          autofocus: true,
          focusNode: focusNode1,
        ),
        const SizedBox(
          height: 10,
        ),
        TextField(
          focusNode: focusNode2,
          autofocus: true,
        ),
        const SizedBox(
          height: 10,
        ),
        ElevatedButton(
            onPressed: () {
              if (focusNode1.hasFocus) {
                focusNode1.nextFocus();
              } else {
                focusNode2.previousFocus();
              }
            },
            child:const Text('切换焦点')),
      ],
    );
  }
}

这是两个 TextField 连续的情况,如果不连续呢?可以用 FocusScope!

把 onPressed 代码修改为:

 ElevatedButton(
      onPressed: () {
        if (focusNode1.hasFocus) {
          FocusScope.of(context).requestFocus(focusNode2);
        } else {
          FocusScope.of(context).requestFocus(focusNode1);
        }
      },
      child: const Text('切换焦点'))

控制字母大小写的 textCapitalization

textCapitalization 用来控制字母的大小写。不常用,了解一下即可。

  • 每个单词的第一字母大写 TextCapitalization.words
  • 第个句子的第一个字母大写 TextCapitalization.sentences
  • 每个字母都大写 TextCapitalization.characters
  • 每个字母默认小写 TextCapitalization.none

可能会用到的是 TextCapitalization.characters,把输入的所有小写字母转换为大写。

TextField(
     textCapitalization: TextCapitalization.characters,
 ),

keyboardType 控制键盘

显示数字键盘TextInputType.number

 TextField(
       keyboardType: TextInputType.number
 );

不显示键盘 TextInputType.none

当 TextField 获得焦点后,不弹出键盘。

更多类型请见 TextInputType

以密码方式显示

TextField(
     obscureText:true
);

一般会加一个象征密码的图标

TextField(
     obscureText:true,
     decoration: InputDecoration(
       prefixIcon: Icon(Icons.lock)
     ),
 );

键盘的操作按钮类型 TextInputAction

默认情况下为 TextInputAction.done。TextField 设置为多行时为 TextInputAction.newline

ios 不能设置 TextInputAction.done,默认为 null。

看文字描述可能不大好理解,还是用TextInputAction.search 举个例子吧。

可能你的手机上显示的是一个搜索图标,

TextField(
 onSubmitted: (value) {
      print(value);
 },
 textInputAction: TextInputAction.search,
)

键盘上原来的 action button 的文本被替换为 搜索。点搜索会触发提交,onSubmitted 被调用。

虽然TextInputAction.search 表明点击按钮后应该进行搜索,但是否执行搜索由开发者决定。

我们再试一下 TextInputAction.newline 的行为。

TextField(
    maxLines: 3,
);

maxLines: 3 可以让 TextField 输入多行,点击 action button 会让 TextField 光标换行。

Flutter 中存在 TextInputAction.newline ,但 Android 或 iOS 中不存在。引入这个术语的原因是开发者可以实现插入新行的通用结果,而无需了解 Android 上的各种 IME 操作和 iOS 上的返回键。

默认的 tooltip 提示语为英文?

如果你新建了一个项目,还没有做国际化,直接用 TextField,tooltip 的提示语是英文。可以这样操作一下:长按 TextField 输入框,这时会弹出一个提示框上面写着 Paste。

这是因为没有设置国际化的原因。

想要使用 flutter_localizations 的话,我们需要在 pubspec.yaml 文件中添加它作为依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
MaterialApp(
 supportedLocales: [
    Locale("en"),
    Locale("zh")
  ],
  localizationsDelegates: [
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate
  ],
  )

maxLength 与 counterText

TextField(
     maxLength: 6,
 ),

counterText 就是显示在右下角的文本。当指定 maxLength 时,会自动显示 counterText,当达到最大字数时,无法再输入。可以用maxLengthEnforcement: MaxLengthEnforcement.none 改变这种限制,字符数超过 maxLength 后也能继续输入。不过,超过限制后,默认会显示红色的 errrorBorder 和 红色的 errorText

TextField(
      maxLength: 3,
      maxLengthEnforcement: MaxLengthEnforcement.none);

maxLengthEnforcement 一共有三个可选值。

  • 没有限制 MaxLengthEnforcement.none
  • 严格限制,超出后无法再输入 MaxLengthEnforcement.enforced
  • 即使在达到最大长度限制后,如果当前值正在撰写,用户仍然可以输入文本。合成结束后,该值将被截断 MaxLengthEnforcement.truncateAfterCompositionEnds

第三条我解释一下。对于中文来说,我们用拼音打字的时候,首先会输入一些字母,这些字母会出现在输入框中,出现想要的汉字后再确认(也可能会自动上屏),让汉字出现在输入框中。字符数超出 maxLength 的时候,第三条允许这些字母出现在输入框中,第二条不允许。

我们用 chrome 浏览查看下效果。打开 chrome 浏览器,输入法调到拼音。要想在地址栏中输入天,需要输入字母 tian,当你输入 t 的时候,t 就已经出现在地址栏中了。随着字母 i,a,n 的输入,候选汉字不断变换,当出现你想到的汉字的时候,按空格(或是其它的上屏按键),地址栏中的字母消失,被 天 替代。如果 maxLength 是 1,MaxLengthEnforcement.enforced 只允许显示 字母 t,MaxLengthEnforcement.truncateAfterCompositionEnds 允许显示全部的 tian,这对于汉字的输入是非常有用的,默认情况下,TextField 的 MaxLengthEnforcement 就是 MaxLengthEnforcement.truncateAfterCompositionEnds

虽然是用 chrome 浏览器地址栏举的例子,TextField 也是一样的。可以用 web 方式在 chrome 中查看 TextField 的表现,效果是一样的。

当 maxLength 为 -1 的时候,表示没有最大限制,输入多少个字符都可以。counterText 只显示当前字符数,不显示最大值。-1 用 TextField.noMaxLength 表示。

TextField(
     maxLength: TextField.noMaxLength ,
 ),

自定义 counterText 样式

默认的 counterText 是很小的,我们想把他变大一些。
两种方法,一个是用 theme,我们这次用局部 theme(相当于 css 中的局部样式表)。先获得父级样式,用 copywith 方法,把新的样式补充进来。当 counterStyle 为 null 的时候,counterText 采用 helperStyle,要想同时影响到 helperText,也可以定义 helperStyle。

    var themeData = Theme.of(context);
    var decorationTheme = themeData.inputDecorationTheme;
    return Theme(
      data: themeData.copyWith(
          inputDecorationTheme:
              decorationTheme.copyWith(counterStyle: TextStyle(fontSize: 20))),
      child: TextField(
        maxLength: 6
      ),
);

另一个方法是直接用 counterStyle

TextField(
      maxLength: 6,
      decoration: InputDecoration(counterStyle: TextStyle(fontSize: 20)),
 );

maxLengthEnforcement: MaxLengthEnforcement.none ,输入字数超过最大字符数后,border ,counterText 都显示为红色。这个时候 counterText 的样式是 errorStyle 控制的。我们可以修改为其它样式。errorStyle 还会影响 errorText 的样式。

我们定义错误时 counterText 显示为绿色。

InputDecoration(
      errorStyle: TextStyle(color: Colors.green),
 );

自定义 counterText

maxLength 设为 3,我们要实现下面的效果:字符数没有超过 3 时 currentCount 样式为黑色,超过 3 时显示为红色。

TextField(
      style: const TextStyle(color: Color(0xFFC45F84), fontSize: 24),
      maxLength: 3,
      maxLengthEnforcement: MaxLengthEnforcement.none,
      decoration: const InputDecoration(counterStyle: TextStyle(fontSize: 20)),
      buildCounter: (context,
          {required  currentLength, required bool  isFocused, maxLength}) {
        return Text.rich(TextSpan(children: [
          TextSpan(
              text: '$currentLength',
              style:  TextStyle(color:currentLength>maxLength!? Colors.red:Colors.black)),
          const TextSpan(text: ' / '),
          TextSpan(text: '$maxLength')
        ]));
      },
    );

用 buildCounter 生成 counterText ,当字符数超过最大限制数后,TextField 不再显示错误状态,border 显示为 focusBorder 样式。

Text.rich ,包含多个 TextSpan,以便加不同的样式。上面的代码实现了所有的功能,但是有一个隐患,当 maxLength 为TextField.noMaxLength 的时候,maxLength 为 -1,不应该直接显示,应该处理一下:当 maxLength 为 -1 的时候,直接不显示了。

if (maxLength >= 0) const TextSpan(text: ' / '),
if (maxLength >= 0) TextSpan(text: '$maxLength')

decoration 的 counterText 与 counter 属性

用 buildCounter 自定义 counterText 完全没有问题,为什么还要弄出这两个属性来呢?我们看下他们的作用。

  • 当 counterText 不为 null ,buildCounter 被忽略。
  • 当 counter 不为 null,counterText 被忽略。

由此可见,如果不想显示 countText,设置 counterText: '' 和定义 buildCounter 的效果一样,字符超出最大限制后,TextField 也不再显示错误状态,border 显示为 focusBorder 样式。

TextField(
      decoration: InputDecoration(
        counterText: ''
      ),
 );

如果要用 counter ,counterText 显示内容的话,不适合动态显示字符数,虽然能实现,但不如用 buildCounter 方便。如果非要显示内容的话,可以放一些提示语什么的,类似于 helperText。

设置 TextField 的显示行数,maxLines 与 maxLines

默认显示一行,超出后横向滚动。不能换行。
调整默认 TextField 默认显示行数的参数有两个 minLines,maxLines。 他们规定了初始显示的行数,但并不限制可以输入的最大行数。

设置 maxLines: 3 ,TextField 默认 显示 3 行,可以从第一行输入,连续输入三行,过超三行,还可以输入,但会坚向滚动。

设置 maxLines: null ,TextField 默认 显示 1 行,可以从第一行输入,连续输入第二行,第三行,无限行,超过约束的最大高度后,坚向滚动。

设置 minLines:2, maxLines:3 ,TextField 默认显示 2 行,可以从第一行输入,连续输入三行,,超过 三行,坚向滚动。

如果设置了 minLines,必须同时设置 maxLines。

自定义验证

abstract class TextInputFormatter {
  
  TextEditingValue formatEditUpdate(
    TextEditingValue oldValue,
    TextEditingValue newValue,
  );

  static TextInputFormatter withFunction(
    TextInputFormatFunction formatFunction,
  ) {
    return _SimpleTextInputFormatter(formatFunction);
  }
}

TextInputFormatter 是一个抽象类,当需要定义自己的子类的时候,只需要 override formatEditUpdate 方法。formatEditUpdate 可以修改 text 的值,比如禁止不被允许的值。 withFunction 是一个快捷函数,让我们直接定义 text 的逻辑,而不必定义新的类。

比如只允许输入 0-9

TextField(
      inputFormatters: [
        TextInputFormatter.withFunction(
          (TextEditingValue oldValue, TextEditingValue newValue) {
            return RegExp(r'^[0-9]*$').hasMatch(newValue.text)
                ? newValue
                : oldValue;
          },
        ),
      ],
);

如果再限制一下长度这样写RegExp(r'^[0-9]{0,10}$') ,只能输入 0 到 10 个数字。

Flutter 为 TextInputFormatter 实现了两个子类,我们可以直接用。

TextField 的 maxLength 功能就是用 LengthLimitingTextInputFormatter 实现的。
FilteringTextInputFormatter 用来过滤字符。

 FilteringTextInputFormatter(
    this.filterPattern, {
    required this.allow,
    this.replacementString = '',
 });

filterPattern 我们一般用 RegExp,就是正则来判断是否匹配。allow 如果为 true,filterPattern 是用于匹配合法的文本,如果为 false,匹配非法的文本。replacementString 是用来替换非法文本的替换字符串。

FilteringTextInputFormatter 有两个命名构造函数,对 allow 进行了赋值,一般情况下,我们优先使用这两个构造函数,让代码更可读。

  FilteringTextInputFormatter.allow(
    Pattern filterPattern, {
    String replacementString = '',
  }) : this(filterPattern, allow: true, replacementString: replacementString);

  FilteringTextInputFormatter.deny(
    Pattern filterPattern, {
    String replacementString = '',
  }) : this(filterPattern, allow: false, replacementString: replacementString);

举个例子,只允许输入 0-9

TextField(
      inputFormatters: [FilteringTextInputFormatter.allow(RegExp('r[0-9]'))],
);

再比如不允许输入 x

TextField(
      inputFormatters: [FilteringTextInputFormatter.deny(RegExp('x'))],
    );

你可能疑问 deny 的参数不是 Patten 吗?怎么能输入字符串?答案是 String 实现了 Patten 接口

abstract class String implements Comparable<String>, Pattern 

替换的功能就不举例子了,可以自己试试。如果需要替换的时候,优先考虑用 FilteringTextInputFormatter。

如果你的验证逻辑很常用,可以 extends 一个子类出来,把逻辑封装起来。还是用只能输入 0-9 这个例子:

class MyFormatter extends TextInputFormatter {
  
  TextEditingValue formatEditUpdate(
      TextEditingValue oldValue, TextEditingValue newValue) {
    return RegExp(r'^[0-9]*$').hasMatch(newValue.text) ? newValue : oldValue;
  }
}

用的时候就会很方便

TextField(
    inputFormatters: [MyFormatter()],
);

onEditingComplete 与 onSubmitted

这两个事件是同时触发的,onEditingComplete 在前,onSubmitted 在后。

如果 onEditingComplete 不设置,默认可能让 TextField 失去焦点。如果不为空,就走 onEditingComplete 的逻辑。

onSubmitted 可以获得 TextFile 的 text。

在下面的例子中,虽然 onEditingComplete 函数体为空,但毕竟不是 null,成功阻止了默认的可能让 TextField 失去焦点的行为。在 onSubmitted 中可以打印 TextField 中的文本。

TextField(
      onEditingComplete: () {},
      onSubmitted: (value) {
        print(value);
      },
);

选中文本

选中文本 还是比较简单的,指定 TextSelection 的开始位置 baseOffset,和结束位置 extentOffset 即可。

下面的例子中,点击按钮会选中全部已经输入的文本。

Column(
      children: [
        TextField(
          controller: controller,
          style: const TextStyle(color: Color(0xFFC45F84), fontSize: 24),
        ),
        ElevatedButton(
            onPressed: () {
              controller.selection = TextSelection(
                  baseOffset: 0, extentOffset: controller.text.length);
            },
            child: Text('选中全部文本'))
      ],
);

最后要了解的是 当 TextField 获得焦点时,它会阻止自己通过 AutomaticKeepAliveClientMixin.wantKeepAlive 进行 dispose,以避免丢失 selection。移除焦点将允许它被disposed。

Flutter TextField 交互实例 到这里就结束了,谢谢观看。

有关Flutter TextField 交互实例 —— 新手礼包的更多相关文章

  1. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  2. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  3. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  4. ruby-on-rails - 如何在 ruby​​ 交互式 shell 中有多行? - 2

    这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式ruby​​shell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f

  5. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  6. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  7. ruby-on-rails - 使用 ruby​​ 将多个实例变量转换为散列的更好方法? - 2

    我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。

  8. ruby - 为什么当我调用类的实例方法时,初始化不显示为方法? - 2

    我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认

  9. ruby - 在 Ruby 中,在类方法的上下文中,什么是实例变量和类变量? - 2

    如果我有以下一段Ruby代码:classBlahdefself.bleh@blih="Hello"@@bloh="World"endend@blih和@@bloh到底是什么?@blih是Blah类中的一个实例变量,@@bloh是Blah类中的一个类变量,对吗?这是否意味着@@bloh是Blah的类Class中的一个变量? 最佳答案 人们似乎忽略了该方法是类方法。@blih将是常量Bleh的类Class实例的实例变量。因此:irb(main):001:0>classBlehirb(main):002:1>defself.blehirb

  10. ruby - 从外部访问类的实例变量 - 2

    我理解(我认为)Ruby中类变量和类的实例变量之间的区别。我想知道如何从该类外部访问该类的实例变量。从内部(即在类方法中而不是实例方法中),它可以直接访问,但是从外部,有没有办法做MyClass.class.[@$#]variablename?我没有任何具体原因要这样做,只是学习Ruby并想知道是否可行。 最佳答案 classMyClass@my_class_instance_var="foo"class上述yield:>>foo我相信Arkku演示了如何从类外部访问类变量(@@),而不是类实例变量(@)。我从这篇文章中提取了上述内

随机推荐