如何在Xamarin中通过Custom Renderer实现Entry获焦时占位符上移动效
Xamarin Entry 占位符上浮动画实现方案(自定义渲染器)
共享层实现自定义Entry控件
首先在共享项目中创建继承自Entry的自定义控件,暴露占位符边距、内边距以及动画时长的可绑定属性:
public class FloatingPlaceholderEntry : Entry { public static readonly BindableProperty PlaceholderMarginProperty = BindableProperty.Create(nameof(PlaceholderMargin), typeof(Thickness), typeof(FloatingPlaceholderEntry), new Thickness(0)); public static readonly BindableProperty PlaceholderPaddingProperty = BindableProperty.Create(nameof(PlaceholderPadding), typeof(Thickness), typeof(FloatingPlaceholderEntry), new Thickness(0)); public static readonly BindableProperty AnimationDurationProperty = BindableProperty.Create(nameof(AnimationDuration), typeof(int), typeof(FloatingPlaceholderEntry), 200); public Thickness PlaceholderMargin { get => (Thickness)GetValue(PlaceholderMarginProperty); set => SetValue(PlaceholderMarginProperty, value); } public Thickness PlaceholderPadding { get => (Thickness)GetValue(PlaceholderPaddingProperty); set => SetValue(PlaceholderPaddingProperty, value); } public int AnimationDuration { get => (int)GetValue(AnimationDurationProperty); set => SetValue(AnimationDurationProperty, value); } }
Android 平台自定义渲染器
在Android项目中创建渲染器,基于原生TextInputLayout实现浮动占位符效果,适配自定义的边距属性:
[assembly: ExportRenderer(typeof(FloatingPlaceholderEntry), typeof(FloatingPlaceholderEntryRenderer))] namespace YourNamespace.Droid.Renderers { public class FloatingPlaceholderEntryRenderer : EntryRenderer { private TextInputLayout _textInputLayout; private FloatingPlaceholderEntry _customEntry; public FloatingPlaceholderEntryRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Entry> e) { base.OnElementChanged(e); if (e.NewElement is not FloatingPlaceholderEntry customEntry || Control == null) return; _customEntry = customEntry; // 用TextInputLayout包裹原生EditText _textInputLayout = new TextInputLayout(Context); _textInputLayout.AddView(Control); SetNativeControl(_textInputLayout); // 开启浮动占位符效果 _textInputLayout.HintEnabled = true; _textInputLayout.Hint = _customEntry.Placeholder; // 设置线性动画 _textInputLayout.SetHintAnimationEnabled(true); // 适配占位符margin和padding _textInputLayout.SetPadding( (int)Context.ToPixels(_customEntry.PlaceholderPadding.Left), (int)Context.ToPixels(_customEntry.PlaceholderPadding.Top), (int)Context.ToPixels(_customEntry.PlaceholderPadding.Right), (int)Context.ToPixels(_customEntry.PlaceholderPadding.Bottom)); Control.SetPadding( (int)Context.ToPixels(_customEntry.PlaceholderMargin.Left), (int)Context.ToPixels(_customEntry.PlaceholderMargin.Top), (int)Context.ToPixels(_customEntry.PlaceholderMargin.Right), (int)Context.ToPixels(_customEntry.PlaceholderMargin.Bottom)); // 自定义动画时长 var hintAnimator = _textInputLayout.HintAnimation; hintAnimator.Duration = _customEntry.AnimationDuration; hintAnimator.Interpolator = new LinearInterpolator(); } } }
iOS 平台自定义渲染器
在iOS项目中创建渲染器重写UITextField的编辑事件,通过原生线性动画实现占位符上浮效果:
[assembly: ExportRenderer(typeof(FloatingPlaceholderEntry), typeof(FloatingPlaceholderEntryRenderer))] namespace YourNamespace.iOS.Renderers { public class FloatingPlaceholderEntryRenderer : EntryRenderer { private UILabel _floatingPlaceholderLabel; private FloatingPlaceholderEntry _customEntry; private CGRect _originalPlaceholderFrame; protected override void OnElementChanged(ElementChangedEventArgs<Entry> e) { base.OnElementChanged(e); if (e.NewElement is not FloatingPlaceholderEntry customEntry || Control == null) return; _customEntry = customEntry; // 隐藏原生占位符,自定义浮动占位符Label Control.Placeholder = string.Empty; _floatingPlaceholderLabel = new UILabel { Text = customEntry.Placeholder, Font = UIFont.SystemFontOfSize(14), TextColor = UIColor.LightGray, Frame = new CGRect( customEntry.PlaceholderMargin.Left + customEntry.PlaceholderPadding.Left, customEntry.PlaceholderMargin.Top + customEntry.PlaceholderPadding.Top, Control.Bounds.Width, Control.Bounds.Height) }; _originalPlaceholderFrame = _floatingPlaceholderLabel.Frame; Control.AddSubview(_floatingPlaceholderLabel); // 绑定编辑事件 Control.EditingDidBegin += OnEditingDidBegin; Control.EditingDidEnd += OnEditingDidEnd; } private void OnEditingDidBegin(object sender, EventArgs e) { if (!string.IsNullOrEmpty(Control.Text)) return; // 线性动画上浮占位符 UIView.Animate(_customEntry.AnimationDuration / 1000.0, 0, UIViewAnimationOptions.CurveLinear, () => { _floatingPlaceholderLabel.Frame = new CGRect( _originalPlaceholderFrame.X, _originalPlaceholderFrame.Y - 20, _originalPlaceholderFrame.Width, 12); _floatingPlaceholderLabel.Font = UIFont.SystemFontOfSize(12); }); } private void OnEditingDidEnd(object sender, EventArgs e) { if (!string.IsNullOrEmpty(Control.Text)) return; // 线性动画恢复占位符位置 UIView.Animate(_customEntry.AnimationDuration / 1000.0, 0, UIViewAnimationOptions.CurveLinear, () => { _floatingPlaceholderLabel.Frame = _originalPlaceholderFrame; _floatingPlaceholderLabel.Font = UIFont.SystemFontOfSize(14); }); } } }
XAML 调用示例
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:custom="clr-namespace:YourNamespace.Controls" x:Class="YourNamespace.MainPage"> <StackLayout Padding="20"> <custom:FloatingPlaceholderEntry Placeholder="请输入用户名" PlaceholderMargin="10,0" PlaceholderPadding="5,0" AnimationDuration="250" HeightRequest="50"/> </StackLayout> </ContentPage>
注意事项
- 如需修改边框样式,可在各平台渲染器中对应设置
TextInputLayout(Android)和UITextField.Layer.BorderWidth(iOS)的属性,避免占位符上浮后和边框重叠 - 占位符上浮后的偏移量可根据实际UI需求自行调整,iOS端当前默认偏移20px
- 若需要支持深色模式,可额外绑定占位符不同状态下的颜色属性
内容的提问来源于stack exchange,提问作者Shashank Mishra
相关产品推荐
相关产品推荐

