Éder Rocha
Éder Rocha

Reputation: 1568

How to prevent a specific Xamarin View up scrolling when soft keyboard is open?

I have the following xamarin layout schemma:

<StackLayout>
    <Grid>...</Grid> <!-- TOP GRID - This must be fixed -->
    <ScrollView>...</ScrollView> <!-- This must scroll  -->
    <Grid><Entry></Entry>/Grid>  <!-- BOTTOM GRID - This must scroll -->
</StackLayout>

iOS behavior is surprisingly as I want(that's a chat layout, so top bar has pic and username which I want to be visible all time), but android behavior is up scrolling the whole page and keeping only Entry and bottom of ScrollView visible, that is almost what I need, but Top Grid is disappearing. Only Srollview and the Bottom grid musts scroll.

How can I achieve this?

EDIT 1

Sample project:

https://wetransfer.com/downloads/46b455cb7148189a0d1f84affa77b7e120210428144025/02b001

Upvotes: 2

Views: 1523

Answers (1)

Anand
Anand

Reputation: 1959

Can you try like this.

The basic Layout structre will be like this.

  <Grid RowDefinitions="50,*,Auto">
            <StackLayout Grid.Row="0" >
                <!-- Username and Profile image -->
            </StackLayout>
            <CollectionView Grid.Row="1">
                <!-- Your COllectionView -->
            </CollectionView>
            <controls:KeyboardView Grid.Row="2">
                <controls:KeyboardView.Margin>
                    <OnPlatform x:TypeArguments="Thickness">
                        <On Platform="iOS" Value="0,0,0,10" />
                        <On Platform="Android" Value="0,0,0,5" />
                    </OnPlatform>
                </controls:KeyboardView.Margin>
                <!-- Your entry and Send Button -->
            </controls:KeyboardView>

        </Grid>

On your PCL, Create KeyboardView

 public class KeyboardView : Grid { }

On ios Create KeyboardViewRenderer

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CoreGraphics;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(KeyboardView), typeof(KeyboardViewRenderer))]
namespace YourAppName.iOS.CustomRender
{
    public class KeyboardViewRenderer : ViewRenderer
    {
        NSObject _keyboardShowObserver;
        NSObject _keyboardHideObserver;
        protected override void OnElementChanged(ElementChangedEventArgs<View> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                RegisterForKeyboardNotifications();
            }

            if (e.OldElement != null)
            {
                UnregisterForKeyboardNotifications();
            }
        }

        void RegisterForKeyboardNotifications()
        {
            if (_keyboardShowObserver == null)
                _keyboardShowObserver = UIKeyboard.Notifications.ObserveWillShow(OnKeyboardShow);
            if (_keyboardHideObserver == null)
                _keyboardHideObserver = UIKeyboard.Notifications.ObserveWillHide(OnKeyboardHide);
        }

        void OnKeyboardShow(object sender, UIKeyboardEventArgs args)
        {

            NSValue result = (NSValue)args.Notification.UserInfo.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
            CGSize keyboardSize = result.RectangleFValue.Size;
            if (Element != null)
            {
                Element.Margin = new Thickness(0, 0, 0, keyboardSize.Height); //push the entry up to keyboard height when keyboard is activated

            }
        }

        void OnKeyboardHide(object sender, UIKeyboardEventArgs args)
        {
            if (Element != null)
            {
                Element.Margin = new Thickness(0); //set the margins to zero when keyboard is dismissed
            }

        }


        void UnregisterForKeyboardNotifications()
        {
            if (_keyboardShowObserver != null)
            {
                _keyboardShowObserver.Dispose();
                _keyboardShowObserver = null;
            }

            if (_keyboardHideObserver != null)
            {
                _keyboardHideObserver.Dispose();
                _keyboardHideObserver = null;
            }
        }
    }
}

For Android add this in App.Xaml

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="KeyboardSample.App"
             xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
             android:Application.WindowSoftInputModeAdjust="Resize">
    <Application.Resources>
    </Application.Resources>
</Application>

UPDATE 1

From Your solution, You need to make 3 Changes.

  1. Remove this line from MainTheme.Base in your style.

    <item name="android:windowFullscreen">true</item>
    
  2. Remove this line from MainActivity Window.SetSoftInputMode(Android.Views.SoftInput.AdjustResize);

  3. Add Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize); After LoadApplication in mainactivity.

UPDATE 2

If removing android:windowFullscreen messed status bar color as well as bottom bar color, We can make use of custom render.

Create an Interface IStatusBarPlatformSpecific

public interface IStatusBarPlatformSpecific
    {
        void SetStatusBarColor(Color color);
        void SetBottomBarColor(Color color);
    }

In android project create render named Statusbar

[assembly: Dependency(typeof(Statusbar))]
namespace YourAppName.Droid.CustomRender.StatusBar_Color
{
    public class Statusbar : IStatusBarPlatformSpecific
    {
        public Statusbar()
        {
        }
        public void SetStatusBarColor(System.Drawing.Color color)
        {
            CrossCurrentActivity.Current.Activity.Window.SetStatusBarColor(ToAndroidColor(color));
        }   
        public void SetBottomBarColor(System.Drawing.Color color)
        {          
            CrossCurrentActivity.Current.Activity.Window.SetNavigationBarColor(ToAndroidColor(color));
        }

        public static Android.Graphics.Color ToAndroidColor(System.Drawing.Color color)
        {
            return new Android.Graphics.Color(color.ToArgb());
        }
    }
}

Now you can call it from your desired pages OnAppearing Like this.

protected override async void OnAppearing()
        {
            base.OnAppearing();
            if (Device.RuntimePlatform == Device.Android)
            {
                var statusbar = 
                 DependencyService.Get<IStatusBarPlatformSpecific>();
                statusbar.SetStatusBarColor(Color.FromHex("#112330"));
                statusbar.SetBottomBarColor(Color.FromHex("#304E62"));
            }

    }

You can now set the status bar color and bottom color matching to your page.Let me know if this worked for you.

Upvotes: 1

Related Questions