属性值
TextField
组件本身具备多种属性,支持很多参数设置来实现不同样式效果。 TextField
组件可直接上手使用,但默认样式和输入规则并不一定是需求开发中想要的(实话说默认样式并不好看)。下面就通过TextField
组件属性介绍来自定义属于自己的输入框吧。
TextField();
基础功能
- obscureText:
true
表示隐藏输入内容,类似密码输入 - readOnly:
true
表示输入框禁止输入 - textCapitalization:控制输入内容大小写(words 首字母大写、sentences 句子首字母大写、characters 所有字母大写),可能存在兼容性问题会不生效。
- minLines & maxLines: 最小行数和最大行数设置,若只设置最大行数输入框高度就是
maxLines
的高度,否则是minLines
的高度。 - maxLength: 输入字符限制器。在输入框下方会出现输入字符数量(一般也不希望透出计数)
样式
样式设置主要未然光标和输入框装饰内容设置。
光标
光标支持自定义,支持设置颜色、高度、宽度、圆角以及是否展示光标。
cursorColor: Colors.black,
cursorHeight: 10,
cursorWidth: 5,
cursorRadius: Radius.circular(10),
showCursor: false,
装饰
前面提到输入框默认样式比较普通,日常开发中需要设置输入框样式。自定义输入框样式是可以通过decoration
来实现的。
- 外框
外框可以使用内置官方已有样式,也能通过自定义边框来实现。
decoration: InputDecoration(
border: OutlineInputBorder()
)
- hint提示
默认未输入内容的提示信息
decoration: InputDecoration(
hintText: "请输入"
)
- 悬浮提示
悬浮提示就是当获取焦点后的输入信息的提示(不太常用)
decoration: InputDecoration(
lableText: "请输入"
)
另外InputDecoration
还支持设置其他border
,例如错误状态、获取交点状态、不可用状态等都能自定义。
软键盘
- textInputAction: 软键盘按钮样式和功能设置(none、unspecified、done、go、search、next等)实现软键盘按钮不同操作能力。
- focusNode: 控制焦点使用,通过它实现弹起或收起软键盘能力。
- autofocus: 是否主动获取焦点,true时在初始化后会自动获取焦点并弹起软键盘(一般情况下是会弹起,特殊情况除外)
输入控制
获取输入内容的方式有两种:onChanged
和controller
。
- onChanged
TextField(
onChanged:(text){
// 这个可以返回输入文本
}
)
- controller
TextEditingController controller = TextEditingController();
controller.addListener((){
// 这个可以返回输入文本
controller.text;
});
TextField(
controller: controller,
)
- inputFormatters: 输入字符限制,通过自定义限制输入内容。
这里举例内置的LengthLimitingTextInputFormatter
用来限制输入字符数。这和基础功能中maxLength
是一样的作用,不同点在于maxLength
会携带计数器。
源码解析
TextField
继承StatefulWidget
,所以就直接来看_TextFieldState
_TextFieldState
_TextFieldState
中的build
方法中实现输入框的展示。在内部多层组件嵌套下(FocusTrapArea
、MouseRegion
等)核心是EditableText
组件。 另外在build
方法中还有switch (theme.platform)
方法,由于各个平台中对于输入框的实现存在差异,例如安卓和iOS输入框的光标以及选择状态样式存在差异。因此这里的switch (theme.platform)
主要就是对于平台差异性做一些设置,代码可见主要是光标和textSelectionControls
文本选择上有所不同。
switch (theme.platform) {
case TargetPlatform.iOS:
final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context);
forcePressEnabled = true;
textSelectionControls ??= cupertinoTextSelectionControls;
paintCursorAboveText = true;
cursorOpacityAnimates = true;
cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionTheme.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorRadius ??= const Radius.circular(2.0);
cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
autocorrectionTextRectColor = selectionColor;
break;
......
case TargetPlatform.android:
case TargetPlatform.fuchsia:
forcePressEnabled = false;
textSelectionControls ??= materialTextSelectionControls;
paintCursorAboveText = false;
cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
break;
EditableTextState
_TextFieldState
中平台差异性判断之后,接下来就是EditableText
组件内部。同样EditableText
是继承自StatefulWidget
,略过之后直接看EditableTextState
。在build
方法中同样是通过多层嵌套(MouseRegion
、Scrollable
、CompositedTransformaTarget
)去看更主要的_Editable
。
_Editable
_Editable
是继承自MultiChildRenderObjectWidget
,绝大多数布局控件都是继承MultiChildRenderObjectWidget
实现的。_Editable
实现的渲染对象是RenderEditable
RenderEditable
RenderEditable
就是RenderBox
,就到了底层布局的测量、绘制、更新的过程。源码分析就直接看paint
方法实现。
_paintContents
部分是绘制文本内容,内部由TextPainter
对文本绘制的实现。
@override
void paint(PaintingContext context, Offset offset) {
_computeTextMetricsIfNeeded();
if (_hasVisualOverflow && clipBehavior != Clip.none) {
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
_paintContents,
clipBehavior: clipBehavior,
oldLayer: _clipRectLayer.layer,
);
} else {
_clipRectLayer.layer = null;
_paintContents(context, offset);
}
_paintHandleLayers(context, getEndpointsForSelection(selection!));
}
_paintHandleLayers
是文本选择器的绘制,若endpoints
== 2则是段落选择。否则只是单选就只有一个光标选择。
void _paintHandleLayers(PaintingContext context, List<TextSelectionPoint> endpoints) {
Offset startPoint = endpoints[0].point;
startPoint = Offset(
startPoint.dx.clamp(0.0, size.width),
startPoint.dy.clamp(0.0, size.height),
);
context.pushLayer(
LeaderLayer(link: startHandleLayerLink, offset: startPoint),
super.paint,
Offset.zero,
);
if (endpoints.length == 2) {
Offset endPoint = endpoints[1].point;
endPoint = Offset(
endPoint.dx.clamp(0.0, size.width),
endPoint.dy.clamp(0.0, size.height),
);
context.pushLayer(
LeaderLayer(link: endHandleLayerLink, offset: endPoint),
super.paint,
Offset.zero,
);
}
}
文末
我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
需要的直接点击文末小卡片可以领取哦我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)
Android学习PDF+架构视频+面试文档+源码笔记
部分资料一览:
- 330页PDF Android学习核心笔记(内含8大板块)
- Android学习的系统对应视频
- Android进阶的系统对应学习资料
- Android BAT大厂面试题(有解析)
领取地址: