Xamarin Android 中自动滚动到编辑文本框





0/5 (0投票)
如何在 Xamarin Android 中自动滚动到编辑文本框
重要提示:这应该适用于较新版本的 Xamarin (2.3.3)。如果由于某种原因无法更新,本文将解释一种解决方法。
引言
在上一篇文章中,我解释了如何在 Xamarin Android 中检测软件键盘的显示和隐藏事件。我在我的项目中这样做是因为某些文本框被软件键盘隐藏,而这些文本框应该编辑其内容。在本文中,我将解释如何在一些较旧版本的 Xamarin 中解决这个问题。
解决方案
首先要做的是检测 Entry
(单行编辑器)和 Editor
(多行编辑器)上的焦点。最好的地方是在渲染器中。如果你的项目中没有这些,你可以创建新的。如果你已经有渲染器,它们可能用于其他目的。一个好主意是将渲染器中引入的所有功能限制在尽可能少的行中,以限制渲染器内部的代码依赖性。因此,我决定通过调用 static
类的 static
方法,将单行控件的滚动功能添加到控件中。以下是 2 个编辑器控件的渲染器。
public class ScrollEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
EditorsScrollingHelper.AttachToControl(Control, this);
}
}
public class ScrollEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
EditorsScrollingHelper.AttachToControl(Control, this);
}
}
EditorsScrollingHelper
中的 AttachToControl
方法应该在控件上发生焦点事件时添加滚动功能。EditorsScrollingHelper
负责此机制的所有繁重工作。
public static void AttachToControl(TextView control, ScrollEditorRenderer renderer)
{
control.FocusChange += (s, e) =>
{
FocusChange(e, renderer.Element);
};
}
public static void AttachToControl(TextView control, ScrollEntryRenderer renderer)
{
control.FocusChange += (s, e) =>
{
FocusChange(e, renderer.Element);
};
}
EditorsScrollingHelper
类的 FocusChange
方法保存实际滚动到焦点控件所需的所有数据。
private static void FocusChange(AndroidView.FocusChangeEventArgs e, XamarinView element)
{
if (e.HasFocus)
{
_focusedElement = element;
_elementHeight = element.Bounds.Height;
}
else _focusedElement = null;
}
需要保存对焦点控件的引用是不言自明的。元素高度将在下面解释。
实际滚动是在 ISoftwareKeyboardService.Show
事件的处理程序内部完成的。处理程序添加在 EditorsScrollingHelper
类的 static
构造函数内部。
static EditorsScrollingHelper()
{
TinyIoCContainer.Current.Resolve<ISoftwareKeyboardService>().Show += OnKeyboardShow;
}
如果之前在 FocusChange
方法内部保存了 _focusedElement
,则 OnKeyboardShow
处理程序会进行滚动。
private static void OnKeyboardShow(object sender, SoftwareKeyboardEventArgs args)
{
if (_focusedElement != null)
{
ScrollIfNotVisible(_focusedElement);
}
}
只有在有焦点控件时,if 条件才会触发滚动逻辑 - 因为 _focusedElement
字段不是 null
。之所以这样做,是因为 Show
事件对于每个控件焦点触发不止一次(因为通过 GlobalLayoutListener
检测软件键盘显示事件并非万无一失)。保存的元素高度在 Show
事件内部用于计算是否需要滚动到焦点控件。
public static void ScrollIfNotVisible(XamarinView element)
{
double translationY = 0;
var parent = element;
while (parent != null)
{
translationY -= parent.Y;
parent = parent.Parent as XamarinView;
}
var height = Application.Current.MainPage.Bounds.Height;
var elementHeight = _elementHeight;
translationY -= elementHeight;
if (-translationY > height)
{
if (Math.Abs(Application.Current.MainPage.TranslationY - translationY) > 0.99)
{
Application.Current.MainPage.SetTranslation(
translationY + height / 2 - elementHeight / 2);
}
}
}
滚动是通过将焦点元素的所有元素高度加起来,从视觉树向上遍历到顶部 - 窗口根目录(没有父控件)。这是焦点控件的实际 Y
坐标。此值与保存的元素高度一起表示焦点控件底部在屏幕上的位置。有了这些信息,我们可以将其与可用的屏幕尺寸(Application.Current.MainPage.Bounds.Height
)进行比较,此时,它应该大约是屏幕的一半(另一半被软件键盘占据)。如果 translationY
值大于应用程序可用的屏幕高度,这意味着控件底部不可见,则应用主页面的平移。平移是通过扩展方法完成的。
public static void SetTranslation(this VisualElement element, double y)
{
element.TranslationY = y;
var rectangle = new Rectangle
{
Left = element.X,
Top = element.Y,
Width = element.Width,
Height = element.Height + (y < 0 ? -y : 0)
};
element.Layout(rectangle);
}
平移的工作原理是将控件向上移动 y
像素,并为此控件设置新的更高矩形。如果没有它,应用程序主页面只会向上移动,但在其下方不会呈现任何内容,除了空白。为了补救这种效果,主页面的矩形更高,并且应用程序会呈现可能位于焦点控件下方的任何控件。
这是滚动应用程序到焦点控件的解决方案的第一部分。另一半是在控件失去焦点时滚回。这是在 SoftwareKeyboardService
中通过在软件键盘隐藏事件上执行额外代码来完成的。
public void InvokeKeyboardHide(SoftwareKeyboardEventArgs args)
{
OnHide();
var handler = Hide;
handler?.Invoke(this, args);
}
主页面平移回原始位置是在 OnHide
方法中完成的。
private void OnHide()
{
if (Application.Current.MainPage != null)
{
Application.Current.MainPage.SetTranslation(0);
}
}
将平移设置为零会将整个应用程序放回其应有的位置。 :)
就是这样!下面是包含工作解决方案的应用程序的 Gif