Android平台Entry/Editor点击:先显光标再执行TapGestureRecognizer并获取光标位置
解决Android平台Entry/Editor点击时获取当前光标位置的问题
核心问题是跨平台TapGestureRecognizer的触发时机早于Android原生控件的光标定位逻辑,导致拿到的是上一次的光标位置。下面是针对Entry和Editor的统一解决方案,直接基于原生触摸事件拦截,确保光标定位完成后再获取正确位置。
实现步骤
1. 共享项目定义附加属性(复用逻辑)
创建一个附加属性,用来在Entry/Editor和ViewModel之间传递光标位置:
using Microsoft.Maui.Controls; namespace YourAppNamespace; public static class CursorPositionHelper { public static readonly BindableProperty TapCursorPositionProperty = BindableProperty.CreateAttached( "TapCursorPosition", typeof(int), typeof(CursorPositionHelper), default(int)); public static int GetTapCursorPosition(BindableObject view) { return (int)view.GetValue(TapCursorPositionProperty); } public static void SetTapCursorPosition(BindableObject view, int value) { view.SetValue(TapCursorPositionProperty, value); } }
2. Android平台实现原生触摸监听
通过自定义Handler(MAUI)或Renderer(Xamarin.Forms),拦截原生触摸事件,在系统完成光标定位后获取位置。
MAUI版本:EntryHandler扩展
using Android.Views; using Microsoft.Maui.Platform; namespace YourAppNamespace.Platforms.Android; public static class EntryCursorHandler { public static void ConfigureCursorPositionListener(this EntryHandler handler) { if (handler.PlatformView is not Android.Widget.EditText editText) return; editText.SetOnTouchListener(new CursorTouchListener(handler.VirtualView)); } private class CursorTouchListener : Java.Lang.Object, IOnTouchListener { private readonly Entry _entry; public CursorTouchListener(Entry entry) { _entry = entry; } public bool OnTouch(View v, MotionEvent e) { if (e.Action == MotionEventActions.Up) { // 用Post把获取操作放到系统光标定位逻辑之后执行 v.Post(() => { if (v is Android.Widget.EditText editText) { int cursorPos = editText.SelectionStart; CursorPositionHelper.SetTapCursorPosition(_entry, cursorPos); } }); } // 返回false,让系统继续处理原生触摸逻辑(显示光标、弹出键盘等) return false; } } }
MAUI版本:EditorHandler扩展
逻辑和Entry完全一致,只是替换对应的控件类型:
using Android.Views; using Microsoft.Maui.Platform; namespace YourAppNamespace.Platforms.Android; public static class EditorCursorHandler { public static void ConfigureCursorPositionListener(this EditorHandler handler) { if (handler.PlatformView is not Android.Widget.EditText editText) return; editText.SetOnTouchListener(new EditorCursorTouchListener(handler.VirtualView)); } private class EditorCursorTouchListener : Java.Lang.Object, IOnTouchListener { private readonly Editor _editor; public EditorCursorTouchListener(Editor editor) { _editor = editor; } public bool OnTouch(View v, MotionEvent e) { if (e.Action == MotionEventActions.Up) { v.Post(() => { if (v is Android.Widget.EditText editText) { int cursorPos = editText.SelectionStart; CursorPositionHelper.SetTapCursorPosition(_editor, cursorPos); } }); } return false; } } }
MAUI注册Handler
在MauiProgram.cs中注册自定义Handler:
using YourAppNamespace.Platforms.Android; namespace YourAppNamespace; public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureMauiHandlers(handlers => { #if ANDROID handlers.AddHandler<Entry, EntryHandler>(handler => { handler.ConfigureCursorPositionListener(); }); handlers.AddHandler<Editor, EditorHandler>(handler => { handler.ConfigureCursorPositionListener(); }); #endif }); return builder.Build(); } }
3. XAML中绑定使用
在Entry/Editor上绑定附加属性,将光标位置同步到ViewModel:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:YourAppNamespace" x:Class="YourAppNamespace.MainPage"> <StackLayout> <Entry local:CursorPositionHelper.TapCursorPosition="{Binding EntryCursorPos, Mode=OneWayToSource}" Placeholder="点击输入框测试光标位置"/> <Editor local:CursorPositionHelper.TapCursorPosition="{Binding EditorCursorPos, Mode=OneWayToSource}" Placeholder="点击编辑器测试光标位置" HeightRequest="150"/> </StackLayout> </ContentPage>
4. ViewModel接收光标位置
在ViewModel中定义对应属性,接收同步过来的光标位置:
using ObservableObject = CommunityToolkit.Mvvm.ComponentModel.ObservableObject; namespace YourAppNamespace; public class MainPageViewModel : ObservableObject { private int _entryCursorPos; public int EntryCursorPos { get => _entryCursorPos; set => SetProperty(ref _entryCursorPos, value); } private int _editorCursorPos; public int EditorCursorPos { get => _editorCursorPos; set => SetProperty(ref _editorCursorPos, value); } }
关键说明
- 放弃使用
TapGestureRecognizer,改用原生触摸监听,避免触发时机冲突。 v.Post()方法确保获取光标位置的逻辑在系统完成光标定位后执行,拿到的是当前点击的正确位置。- 返回
false保留原生控件的所有默认行为(显示光标、弹出键盘、长按选中等)。 - 附加属性的方式让Entry和Editor复用同一套逻辑,无需单独编写事件处理。
内容的提问来源于stack exchange,提问作者osama alqaisi
相关产品推荐
相关产品推荐

