繁体   English   中英

出现键盘时调整屏幕的一部分

[英]Resize one part of screen when keyboard appears

我想在键盘出现时调整屏幕的一部分大小。

我要这个:

没有键盘: 在此处输入图片说明

当键盘出现时: 在此处输入图片说明

所以我只想调整 ListView 的大小,如果我选择 EditText1 或 EditText2,我想同时看到两者。 如果选择 ET1,我也需要查看 ET2。

现在我有这个 XAML,但是当键盘出现时我的所有屏幕都向上滚动,当我选择 ET1 时,ET2 位于键盘后面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="5dp"
            android:orientation="vertical">
            <TextView
                android:layout_width="wrap_content"
                android:layout_marginTop="10dp"
                android:paddingBottom="10dp"
                android:layout_gravity="center"
                android:layout_height="wrap_content"
                android:textColor="@android:color/black"
                android:textStyle="bold" />
            <View
                android:layout_width="match_parent"
                android:layout_height="3dp"
                android:background="@android:color/black"/>
        </LinearLayout>
    </android.support.v7.widget.CardView>

    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="170dp">
        <ListView
            android:background="@drawable/border_shadow"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:importantForAccessibility="auto"
            android:isScrollContainer="true"/>
    </RelativeLayout >

    <LinearLayout 
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp">
        <LinearLayout 
            android:orientation="vertical"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <AutoCompleteTextView
                android:layout_width="100dp"
                android:singleLine="true"
                android:layout_height="wrap_content"
                android:textColor="@android:color/white" />
            <AutoCompleteTextView
                android:singleLine="true"
                android:layout_width="100dp"
                android:textColor="@android:color/white"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>

    <LinearLayout 
        android:orientation="horizontal"
        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:layout_marginTop="120dp"
        android:layout_height="wrap_content">
        <Button
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:shadowColor="#00000000"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="10dp"
            android:background="@drawable/button"
            android:textColor="@android:color/white"
            android:clickable="true"/>
        <Button
            android:layout_width="100dp"
            android:layout_marginLeft="10dp"
            android:layout_height="50dp"
            android:shadowColor="#00000000"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            android:background="@drawable/button"
            android:textColor="@android:color/white"
            android:clickable="true"/>
    </LinearLayout>

</LinearLayout>

我需要改变什么?

在此处输入图片说明

Create One class Name AlignLayoutDesign

import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.widget.FrameLayout;


public class AlignLayoutDesign
{
   private View mChildOfContent;
   private int usableHeightPrevious;
   private FrameLayout.LayoutParams frameLayoutParams;
   private Rect contentAreaOfWindowBounds = new Rect();

public AlignLayoutDesign(Activity activity)
{
    FrameLayout content = activity.findViewById(android.R.id.content);
    mChildOfContent = content.getChildAt(0);
    mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(this::possiblyResizeChildOfContent);
    frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}

private void possiblyResizeChildOfContent()
{
    int usableHeightNow = computeUsableHeight();
    if (usableHeightNow != usableHeightPrevious)
    {
        int heightDifference = 0;
        if (heightDifference > (usableHeightNow /4))
        {
            // keyboard probably just became visible
            frameLayoutParams.height = usableHeightNow - heightDifference;
        }
        else
        {
            // keyboard probably just became hidden
            frameLayoutParams.height = usableHeightNow;
        }
        mChildOfContent.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
        mChildOfContent.requestLayout();
        usableHeightPrevious = usableHeightNow;
    }
}

private int computeUsableHeight()
{
    mChildOfContent.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
    return contentAreaOfWindowBounds.height();
}
}

  --------------------------------------------------

  Then class this class in your Activity or Fragment where you want a Layout 

    setContentView(R.layout.your_layout);
    AlignLayoutDesign(this);

不幸的是,谷歌用Android制作了真正的“键盘晚餐”,却顽固地拒绝对其进行纠正。 他们拒绝添加用于更改键盘状态或获取键盘大小的方法的回调。 此外,他们处理可见屏幕的大小调整和平移的方式不一致,并且容易出现错误。 在您的情况下,理想的情况是在键盘打开或关闭时触发一个事件,该事件包含键盘的大小。 尝试以下我创建的实用程序服务。 它不是完美的,但即使在调试模式下并使用硬件键盘也能正常工作,并且使您可以根据需要灵活地进行调整。

public class KeyboardStatusService
{

    public delegate void KeyboardStatusChangeEventHandler(KeyboardStatusChangeEventArgs e);
    public event KeyboardStatusChangeEventHandler KeyboardStatusChangeEvent;

    private int _openHeightDelta = -1;
    private int _closedHeightDelta = 0;

    private InputMethodManager __imm = null;
    private InputMethodManager _imm
    {
        get
        {
            if (__imm == null || __imm.Handle == IntPtr.Zero)
                __imm = BaseApplication.CurrentActivity?.GetSystemService(Context.InputMethodService) as InputMethodManager;
            return __imm;
        }
    }


    public KeyboardStatus Status { get; private set; } = KeyboardStatus.Closed;  // Default to closed when the app starts

    public bool Subscribed { get; private set; } = false;

    // Threshold used to remove false positives
    public int Threshold { get; set; } = 144;    

    public void Subscribe()
    {
        Subscribe(BaseApplication.CurrentActivity);
    }

    public void Subscribe(Activity activity)
    {
        if (!Subscribed)
        {
            var rootLayout = activity?.Window.DecorView; //.FindViewById(global::Android.Resource.Id.Content);
            if (rootLayout != null)
            {
                // Subscribe to ViewTreeObserver.GlobalLayout
                rootLayout.ViewTreeObserver.GlobalLayout += LayoutChangeDetected;
                Subscribed = true;  // so we only ever subscribe once
            }
        }
    }

    private async void LayoutChangeDetected(object sender, EventArgs e)
    {
        await Task.Run(() => { LayoutChangeDetected(); }).ConfigureAwait(false);
    }


    private void LayoutChangeDetected()
    {
        var open = _imm.IsAcceptingText;
        if (Status == KeyboardStatus.Closed && open)  // was closed. now open
        {
            var rootLayout = BaseApplication.CurrentActivity.Window.DecorView;
            Rect r = new Rect();
            rootLayout.GetWindowVisibleDisplayFrame(r);
            Rect r1 = new Rect();
            rootLayout.GetLocalVisibleRect(r1);
            var heightDelta = r1.Bottom - r.Bottom;

            // Double check (in case we have manually changed layouts in response to the keyboard opening and closing
            if (heightDelta > _closedHeightDelta + Threshold)  // may need to add padding here to account for other layout changes
            {
                if (_openHeightDelta == -1)
                    _openHeightDelta = heightDelta;
                Status = KeyboardStatus.Open;
                // Trigger the event
                KeyboardStatusChangeEvent?.Invoke(new KeyboardStatusChangeEventArgs(Status, _openHeightDelta));
            }

        }
        else if (Status == KeyboardStatus.Open)
        {
            if (!open)  // was open. now closed -  this handles when an action results in EditText losing focus and input ability
            {
                Status = KeyboardStatus.Closed;
                // Trigger the event
                KeyboardStatusChangeEvent?.Invoke(new KeyboardStatusChangeEventArgs(Status, _closedHeightDelta));
            }
            else
            {
                // some actions don't result in edit Text losing focus or input ability, such as keyboard dismiss button.
                // This may be limited to when a hardware keyboard is attached, such as when debugging
                var rootLayout = BaseApplication.CurrentActivity.Window.DecorView;
                Rect r = new Rect();
                rootLayout.GetWindowVisibleDisplayFrame(r);
                Rect r1 = new Rect();
                rootLayout.GetLocalVisibleRect(r1);
                var heightDelta = r1.Bottom - r.Bottom;
                if  (heightDelta < _openHeightDelta - Threshold)  // may need to add padding here to account for other layout changes
                {
                    _closedHeightDelta = heightDelta;
                    Status = KeyboardStatus.Closed;
                    // Trigger the event
                    KeyboardStatusChangeEvent?.Invoke(new KeyboardStatusChangeEventArgs(Status, _closedHeightDelta));
                }
            }
        }
    }


    public void Unsubscribe()
    {
        Unsubscribe(BaseApplication.CurrentActivity);
    }
    public void Unsubscribe(Activity activity)
    {
        var rootLayout = activity?.Window.DecorView; //.FindViewById(global::Android.Resource.Id.Content);
        if (rootLayout != null)
        {
            rootLayout.ViewTreeObserver.GlobalLayout -= LayoutChangeDetected;  // Don't need to check for subscribed as wont cause an issue
            Subscribed = false;
        }
    }


    public static void DismissKeyboard(Activity activity, View view)
    {
        if (view == null)
            view = new View(activity);
        InputMethodManager iMM = (InputMethodManager)activity.GetSystemService(Context.InputMethodService);
        iMM.HideSoftInputFromWindow(view.WindowToken, HideSoftInputFlags.None);
        view.ClearFocus();
    }
    public static void DismissKeyboard(Context ctx, View view)
    {
        if (view == null)
            view = new View(ctx);
        InputMethodManager iMM = (InputMethodManager)ctx.GetSystemService(Context.InputMethodService);
        iMM.HideSoftInputFromWindow(view.WindowToken, HideSoftInputFlags.None);
        view.ClearFocus();
    }
    public static void DismissKeyboard(Context ctx, Fragment fragment)
    {
        var view = fragment.View.RootView;
        InputMethodManager iMM = (InputMethodManager)ctx.GetSystemService(Context.InputMethodService);
        iMM.HideSoftInputFromWindow(view.WindowToken, HideSoftInputFlags.None);
        view.ClearFocus();
    }
    public static void DismissKeyboard(Context ctx, global::Android.Support.V4.App.Fragment fragment)
    {
        var view = fragment.View.RootView;
        InputMethodManager iMM = (InputMethodManager)ctx.GetSystemService(Context.InputMethodService);
        iMM.HideSoftInputFromWindow(view.WindowToken, HideSoftInputFlags.None);
        view.ClearFocus();
    }
    public static void DismissKeyboard(Fragment fragment)
    {
        var view = fragment.View.RootView;
        InputMethodManager iMM = (InputMethodManager)fragment.Activity.GetSystemService(Context.InputMethodService);
        iMM.HideSoftInputFromWindow(view.WindowToken, HideSoftInputFlags.None);
        view.ClearFocus();
    }
    public static void DismissKeyboard(global::Android.Support.V4.App.Fragment fragment)
    {
        var view = fragment.View.RootView;
        InputMethodManager iMM = (InputMethodManager)fragment.Activity.GetSystemService(Context.InputMethodService);
        iMM.HideSoftInputFromWindow(view.WindowToken, HideSoftInputFlags.None);
        view.ClearFocus();
    }
    public static void DismissKeyboard(Activity activity)
    {
        var view = activity.FindViewById(global::Android.Resource.Id.Content).RootView;
        InputMethodManager iMM = (InputMethodManager)activity.GetSystemService(Context.InputMethodService);
        iMM.HideSoftInputFromWindow(view.WindowToken, HideSoftInputFlags.None);
        view.ClearFocus();
    }
    public static void ToggleKeyboard(Activity activity)
    {
        InputMethodManager iMM = (InputMethodManager)activity.GetSystemService(Context.InputMethodService);
        iMM.ToggleSoftInput(ShowFlags.Implicit, HideSoftInputFlags.ImplicitOnly);
    }
    public static void ShowKeyboard(Activity activity, View view, bool forced = false)
    {
        if (view == null)
            view = new View(activity);
        InputMethodManager iMM = (InputMethodManager)activity.GetSystemService(Context.InputMethodService);
        if (forced)
            iMM.ShowSoftInput(view, ShowFlags.Forced);
        else
            iMM.ShowSoftInput(view, ShowFlags.Implicit);
    }
    public static void ShowKeyboard(Context ctx, View view, bool forced = false)
    {
        if (view == null)
            view = new View(ctx);
        InputMethodManager iMM = (InputMethodManager)ctx.GetSystemService(Context.InputMethodService);
        if (forced)
            iMM.ShowSoftInput(view, ShowFlags.Forced);
        else
            iMM.ShowSoftInput(view, ShowFlags.Implicit);
    }

}

public enum KeyboardStatus
{
    Closed,
    Open,
    Unknown
}

public class KeyboardStatusChangeEventArgs : EventArgs
{
    public KeyboardStatus Status { get; private set; } = KeyboardStatus.Unknown;
    public int VisibleHeightToDecorHeightDelta { get; private set; } = -1;
    public KeyboardStatusChangeEventArgs(KeyboardStatus status, int visibleHeightToDecorHeightDelta)
    {
        Status = status;
        VisibleHeightToDecorHeightDelta = visibleHeightToDecorHeightDelta;
    }
}

然后在您的活动中订阅/取消订阅,如下所示:

private KeyboardStatusService KeyboardStatus { get; } = new KeyboardStatusService();
private int _keyboardHeight;

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    KeyboardStatus.Subscribe(this);
    KeyboardStatus.KeyboardStatusChangeEvent += OnKeyboardStatusChanged;
    ...........
}

protected override void OnDestroy()
{
    base.OnDestroy();
    KeyboardStatus.KeyboardStatusChangeEvent -= OnKeyboardStatusChanged;
    KeyboardStatus.Unsubscribe(this);
    ........
}

private void OnKeyboardStatusChanged(KeyboardStatusChangeEventArgs e)
{
    var keyboardStatus = e.Status;
    if (keyboardStatus == KeyboardStatus.Open)
    {
        var heightDelta = e.VisibleHeightToDecorHeightDelta;
        // need to adjust keyboard height calculation based upon the prescribed adjustment for the Activity set as  WindowSoftInputMode
        // the presence of a toolbar and.or status bar
        _keyboardHeight = heightDelta - YourToolBarHeight - YourStatusBarHeight;

        // ADJUST THE HEIGHT OF YOUR VIEW HERE

    }
    else
    {
        // ADJUST THE HEIGHT OF YOUR VIEW HERE
    }
}

这是BaseApplication:

public partial class BaseApplication: Application, Application.IActivityLifecycleCallbacks
{
    public int NumberActivitiesActive { get; protected set; }

    public static ApplicationState ApplicationState { get; protected set; } = ApplicationState.NotRunning;

    public static DateTime AppEnteredTime { get; protected set; } = DateTime.MinValue;
    public static DateTime AppLeftTime { get; protected set; } = DateTime.MinValue;

    public static double TimeInAppMs
    {
        get
        {
            if (AppLeftTime == DateTime.MinValue)
                return DateTime.Now.Subtract(AppEnteredTime).TotalMilliseconds;
            else
                return AppLeftTime.Subtract(AppEnteredTime).TotalMilliseconds;
        }
    }


    public static Activity CurrentActivity { get; private set; }


    // Used to store activities to be excluded from counts, such as Activities used to handle notifications in response to push notifications
    public static List<string> ExcludedActivities { get; } = new List<string>();

    public static void RegisterExcludedActivity(string activityName)
    {
        if (!ExcludedActivities.Contains(activityName))
            ExcludedActivities.Add(activityName);
    }
    public static void UnregisterExcludedActivity(string activityName)
    {
        ExcludedActivities.Remove(activityName);
    }


    public BaseApplication(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { }


    public override void OnCreate()
    {
        base.OnCreate();
        RegisterActivityLifecycleCallbacks(this);
        ApplicationState = ApplicationState.NotRunning;
    }

    public override void OnTerminate()
    {
        base.OnTerminate();
        UnregisterActivityLifecycleCallbacks(this);
        ApplicationState = ApplicationState.NotRunning;
    }


    public virtual void OnActivityCreated(Activity activity, Bundle savedInstanceState)
    {
        CurrentActivity = activity;
    }

    public virtual void OnActivityDestroyed(Activity activity) // Exclude this activity because it only relates to a notification created in response to a  push notification
    {
        if (!ExcludedActivities.Contains(activity.LocalClassName) && NumberActivitiesActive <= 0)
            ApplicationState = ApplicationState.NotRunning;
    }

    public virtual void OnActivityPaused(Activity activity)
    {
    }

    public virtual void OnActivityResumed(Activity activity)
    {
        CurrentActivity = activity;
    }

    public virtual void OnActivitySaveInstanceState(Activity activity, Bundle outState)
    {
    }

    public virtual void OnActivityStarted(Activity activity)
    {
        CurrentActivity = activity;
        if (!ExcludedActivities.Contains(activity.LocalClassName))
        {
            if (NumberActivitiesActive == 0)
            {
                AppEnteredTime = DateTime.Now;
                ApplicationState = ApplicationState.Foreground;
            }
            NumberActivitiesActive++;
        }
    }

    public virtual void OnActivityStopped(Activity activity)
    {
        if (!ExcludedActivities.Contains(activity.LocalClassName))
        {
            NumberActivitiesActive--;
            if (NumberActivitiesActive == 0)
            {
                AppLeftTime = DateTime.Now;
                if (activity.IsFinishing)
                    ApplicationState = ApplicationState.NotRunning;
                else
                    ApplicationState = ApplicationState.Background;
            }
        }
    }
}

public enum ApplicationState
{
    Background,
    Foreground,
    NotRunning,
    Unknown
}

要使用基本应用程序,您需要声明一个从项目中的基本应用程序继承的MainApplication,并为其提供[Application]批注,如下所示:

#if DEBUG
    [Application(Debuggable = true)]
#else
    [Application(Debuggable = false)]
#endif
    public partial class MainApplication : BaseApplication
    {
        public MainApplication(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { }
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM