可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Using Xamarin Forms, consider the Xaml below.
<StackLayout VerticalOptions="FillAndExpand">
<Image x:Name="cameraImage" Source="camera.png" />
<Label Text="Describe the image" />
<Editor />
<Button Text="Save" />
</StackLayout>
This renders an image, an editor and a save button. The image is in 4x3 image ratio and covers about a third of the available screen height. The editor is rendered below.
The problem is that the keyboard covers the Editor in iOS. A standard iOS issue normally.
The question is: What is the Xamarin Forms way of handling this?
Thanks
// Johan
回答1:
To get auto scroll for Editors and Entries with Xamarin.Forms, you usually just have to pack your View, in this case the StackLayout, into a ScrollView:
<ScrollView>
<StackLayout VerticalOptions="FillAndExpand">
<Image x:Name="cameraImage" Source="camera.png" />
<Label Text="Describe the image" />
<Editor />
<Button Text="Save" />
</StackLayout>
</ScrollView>
That's how it's supposed to work, but as of today (June 2014) there's a bug preventing this to work fully with the Editor (it works well with Entries). The issue is known and is worked on.
[UPDATE 2014-11-20]The issue has been addressed, and will be available in the next -pre release of XF 1.3
回答2:
I just stumbled upon this question when writing a tiny chat application, which basically contains a scrollable message list, a text entry and a send button:
The problem with the previously posted solution is that you'd need to nest two scroll views, which is not recommended by the Xamarin.Forms documentation. To prevent the keyboard from hiding the entry, I found the following hack:
I'm adding a placeholder
at the end of the main stack layout. Depending on whether the entry is focussed (i.e. the keyboard is visible or not) the height of the placeholder is set to 0 or the keyboard height.
// HACK: make entry visible when keyboard open
var placeholder = new BoxView {
HeightRequest = 0,
};
entry.Focused += (sender, e) => placeholder.HeightRequest = 210;
entry.Unfocused += (sender, e) => placeholder.HeightRequest = 0;
Content = new StackLayout {
VerticalOptions = LayoutOptions.Fill,
Padding = 5,
Children = {
whoTable,
messageScrollView,
new StackLayout {
Orientation = StackOrientation.Horizontal,
VerticalOptions = LayoutOptions.End,
HeightRequest = 70,
Children = {
entry,
sendButton,
},
},
placeholder,
},
};
Of course, this is not perfect. Especially the hard-coded keyboard height should be implemented more elegantly. And probably you should apply it on iOS only, not on Android.
回答3:
Sometimes you cannot put your main view in a scrollview, in those cases you can implement this by handling the keyboard events the iOS project and passing them to the Forms level. Android takes care of it's self.
using System;
using Foundation;
using UIKit;
using RaiseKeyboard.iOS;
[assembly: Xamarin.Forms.Dependency (typeof (KeyboardHelper))]
namespace RaiseKeyboard.iOS
{
// Raises keyboard changed events containing the keyboard height and
// whether the keyboard is becoming visible or not
public class KeyboardHelper : IKeyboardHelper
{
public KeyboardHelper() {
NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, OnKeyboardNotification);
NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification);
}
public event EventHandler<KeyboardHelperEventArgs> KeyboardChanged;
private void OnKeyboardNotification (NSNotification notification)
{
var visible = notification.Name == UIKeyboard.WillShowNotification;
var keyboardFrame = visible
? UIKeyboard.FrameEndFromNotification(notification)
: UIKeyboard.FrameBeginFromNotification(notification);
if (KeyboardChanged != null) {
KeyboardChanged (this, new KeyboardHelperEventArgs (visible, (float)keyboardFrame.Height));
}
}
}
}
Then at the Forms level:
using System;
using Xamarin.Forms;
namespace RaiseKeyboard
{
// Provides static access to keyboard events
public static class KeyboardHelper
{
private static IKeyboardHelper keyboardHelper = null;
public static void Init() {
if (keyboardHelper == null) {
keyboardHelper = DependencyService.Get<IKeyboardHelper>();
}
}
public static event EventHandler<KeyboardHelperEventArgs> KeyboardChanged {
add {
Init();
keyboardHelper.KeyboardChanged += value;
}
remove {
Init ();
keyboardHelper.KeyboardChanged -= value;
}
}
}
public interface IKeyboardHelper
{
event EventHandler<KeyboardHelperEventArgs> KeyboardChanged;
}
public class KeyboardHelperEventArgs : EventArgs
{
public readonly bool Visible;
public readonly float Height;
public KeyboardHelperEventArgs(bool visible, float height) {
Visible = visible;
Height = height;
}
}
}
If you are working in a Stacklayout and wish to raise the view above the keyboard you can put a spacer with a height 0 at the bottom of the stack. Then set it to the height of the keyboard when the keyboard changed event is raised.
spacer.HeightRequest = e.Visible ? e.Height : 0;
If you are working with a Listview you can handle this by translating your view by the amount it would have been overlapped by.
bottomOffset = mainStack.Bounds.Bottom - textStack.Bounds.Bottom;
textStack.TranslationY -= e.Visible ? e.Height - bottomOffset : bottomOffset - e.Height;
Listviews have to be handled differently as there height is automatically adjusted by Forms and using a spacer results over correction.
Sample here: https://github.com/naturalistic/raisekeyboard
回答4:
Expanding on the answer from @Falko, you can check the platform for iOS since Android handles this as expected natively.
I also added this to the page for quick and dirty orientation via this answer.
static bool IsPortrait(Page p) { return p.Width < p.Height; }
Anyway, I understand that Xamarin are adding some solutions for this soon. For now, though...
protected async void Message_Focused(object sender, EventArgs args)
{
if (Device.OS == TargetPlatform.iOS)
{
//TLR: still need a way to determine the iOS keyboard's height first
//until then, this is a functional hack
if (IsPortrait(this))
{
KeyboardSpacer.HeightRequest = 165;
}
else
{
KeyboardSpacer.HeightRequest = 114;
}
}
}
protected async void Message_Unfocused(object sender, EventArgs args)
{
if (Device.OS == TargetPlatform.iOS)
{
KeyboardSpacer.HeightRequest = 0;
}
}
回答5:
Be careful, the raisekeyboard
was just implemented for one entry in the application, if you add a new Entry, the KeyboardHelper.KeyboardChanged
is going to shoot, when the focus is in any entry.
KeyboardHelper.KeyboardChanged += (sender, e) =>{
bottomOffset = this.ParentView.Bounds.Bottom - _editor.Bounds.Bottom;
if (KeyboardStatus)
_editor.TranslationY = e.Visible ? -(e.Height - bottomOffset) : 0;
else
_editor.TranslationY = 0;
};
回答6:
Using ScrollView as mentioned above fixes problem in iOS and partially fixes in Android. To fully fix problem in android, I found another simple and nice addition.
Only in android I replace Xamarin forms Editor with android specific TextEdit control. So in my Page constructor I have following code just for android.
#if __ANDROID__
// I have editor defined in xaml named ReportTextEditor in stacklayout named MainStackLayout
Editor sharedEditor = this.ReportTextEditor;
MainStackLayout.Children.Remove(sharedEditor); //removing the ReportTextEditor which was defined in xaml
EditText editText = new EditText(Forms.Context); //created android specific editor
editText.InputType = InputTypes.ClassText | InputTypes.TextFlagAutoCorrect | InputTypes.TextFlagCapSentences | InputTypes.TextFlagMultiLine; //keyboard options, optional
MainStackLayout.Children.Add(editText); //Added android specific edit text to my stack layout
#endif
You also need to add android specific namespaces as needed.
#if __ANDROID__
using Xamarin.Forms.Platform.Android;
using Android.Widget;
using Button = Xamarin.Forms.Button;
using Android.Text;
#endif