键盘隐藏/可见时调整Scrollview高度

时间:2016-01-31 16:32:44

标签: android keyboard scrollview

我有一组带有控件的活动(EditText,Spinner等),其中一个EditText有一个自定义键盘。这是我的XML的样子

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/masterRelativeLayout"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ScrollView
        android:id="@+id/mainScrollview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fillViewport="true" >

        <LinearLayout
            android:id="@+id/childLinearLayout"
            android:layout_width="match_parent"
            android:orientation="vertical"
            android:layout_height="wrap_content">

            <!-- Many EditText's, Spinners here -->

        </LinearLayout>
    </ScrollView>

    <android.inputmethodservice.KeyboardView
        android:id="@+id/myKeyboardView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:keyPreviewLayout ="@layout/kbdpreview"
        android:layout_alignParentBottom="true"
        android:visibility="gone" />
</RelativeLayout>

请注意,键盘视图底部对齐,因此它显示在屏幕底部。

自定义键盘被隐藏并显示在特定的EditText上,如下所示:

当触摸活动中的特定EditText时,将调用这些方法。

private OnTouchListener m_onTouchListenerNotationText = new OnTouchListener()
{
    @Override
    public boolean onTouch(View v, MotionEvent event)
    {
        if (v == m_notationText)
        {
            // Hide the default keyboard
            getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
            m_notationText.onTouchEvent(event);
            m_customKeyboard.showCustomKeyboard(v);
            AdjustScrollView();
            return true;    // Done with the event
        }
        return false;
    }
};

每当屏幕上出现自定义键盘时,它都会隐藏我视图中的某些控件。但是,Android框架不知道此键盘正在显示,因此scrollview无法调整。控件仍隐藏在自定义键盘后面。因此,我在自定义键盘中实现了一个回调,当键盘可见时会调用它。

// This listener is in showCustomKeyboard(...) function
// Set a static variable m_height and tell the activity to adjust the scrollview after keyboard becomes VISIBLE
mKeyboardView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
    @SuppressLint("NewApi")
    @SuppressWarnings("deprecation")
    @Override
    public void onGlobalLayout()
    {
        int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN)
        {
            mKeyboardView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
        else
        {
            mKeyboardView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
        m_height = mKeyboardView.getHeight();
        Log.d(TAG, "Custom Keyboard now visible, height = " + m_height);
        mHostActivity.AdjustScrollView();
    }
});

// This listener is in hideCustomKeyboard(...) function
// Set a static variable m_height and tell the activity to adjust the scrollview after keyboard becomes HIDDEN
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
    @SuppressLint("NewApi")
    @SuppressWarnings("deprecation")
    @Override
    public void onGlobalLayout()
    {
        int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN)
        {
            view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
        else
        {
            view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
        m_height = 0;
        Log.d(TAG, "Custom Keyboard now hidden");
        mHostActivity.AdjustScrollView();
    }
});

最后,滚动视图调整如下:

public synchronized void AdjustScrollView()
{
    RelativeLayout rl = (RelativeLayout) findViewById(R.id.masterRelativeLayout);
    int masterHeight = rl.getHeight();
    Log.d(TAG, "Scrollview master relative layout height = " + masterHeight);

    LinearLayout ll = (LinearLayout) findViewById(R.id.childLinearLayout);
    int childHeight = ll.getHeight();
    Log.d(TAG, "Scrollview child linear layout height = " + childHeight);

    ScrollView scrollView = (ScrollView) findViewById(R.id.mainScrollview);
    if (masterHeight - childHeight - m_customKeyboard.m_height <= 0)
    {
        // Need to adjust scrollview's height
        scrollView.getLayoutParams().height = masterHeight - m_customKeyboard.m_height;
        Log.d(TAG, "Setting scrollview.layoutparams.height = " + (masterHeight - m_customKeyboard.m_height));
    }
    else if (m_customKeyboard.m_height == 0)
    {
        // Need to adjust scrollview's height to full screen
        scrollView.getLayoutParams().height = childHeight;
        Log.d(TAG, "Setting scrollview.layoutparams.height = " + childHeight);
    }
}

AndroidManifest.xml对此活动有以下行:

android:windowSoftInputMode="stateAlwaysHidden|stateUnchanged|adjustResize"

这主要有效,有两个问题:

问题1:当我长按显示自定义键盘的EditText时,有时会出现竞争条件,即默认键盘 AND 自定义键盘都会暂时显示,然后默认键盘消失(因为我将其隐藏在m_onTouchListenerNotationText中)。但是在这个时候,AdjustScrollView的计算搞砸了,因为现在,主相对布局高度非常小(总高度 - 自定义键盘高度 - 默认键盘高度)。所以上面的scrollview高度计算是错误的。现在,scrollview被限制在屏幕顶部的一个非常小的区域,然后是空白区域,然后是我底部的自定义键盘。我通过在EditText的触摸侦听器中添加对AdjustScrollView的调用来解决这个问题(因此,只要发生这种情况,用户就可以单击该微小视图,触摸侦听器将调整scrollview)。这种解决方法是不可取的,因为它使得长按无用(我希望用户能够长按以显示剪切/复制/粘贴等的默认系统菜单)。需要额外触摸的解决方法使得系统菜单消失。

After long press in the EditText - Problem 1

问题2:如果隐藏了自定义键盘,则滚动视图不会再次占用整个屏幕尺寸。屏幕的底部部分(由自定义键盘占用)保持空白,滚动视图仅位于屏幕的顶部。

Problem 2

1 个答案:

答案 0 :(得分:0)

我找到的唯一解决方案是使用键盘hide-show检测,如下例所示:

https://stackoverflow.com/a/7423586/1979882

概念是将背景布局覆盖为您的自定义和度量。

public class VLinearLayoutKeyboardListener extends LinearLayout {
     public interface IKeyboardChanged {
            void onKeyboardShown(int actualHeight, int proposedheight);
            void onKeyboardHidden(int actualHeight, int proposedheight);
        }
     private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public VLinearLayoutKeyboardListener(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public VLinearLayoutKeyboardListener(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VLinearLayoutKeyboardListener(Context context) {
        super(context);
    }
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public VLinearLayoutKeyboardListener(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        // TODO Auto-generated constructor stub
    }

     public void addKeyboardStateChangedListener(IKeyboardChanged listener) {
            keyboardListener.add(listener);
        }

        public void removeKeyboardStateChangedListener(IKeyboardChanged listener) {
            keyboardListener.remove(listener);
        }
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
            final int actualHeight = getHeight();

            if (actualHeight > proposedheight) {
                notifyKeyboardShown(actualHeight, proposedheight);
            } else if (actualHeight < proposedheight) {
                notifyKeyboardHidden(actualHeight, proposedheight);
            }
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

        private void notifyKeyboardHidden(int actualHeight, int proposedheight) {
            for (IKeyboardChanged listener : keyboardListener) {
                listener.onKeyboardHidden(actualHeight, proposedheight);
            }
        }

        private void notifyKeyboardShown(int actualHeight, int proposedheight) {
            for (IKeyboardChanged listener : keyboardListener) {
                listener.onKeyboardShown(actualHeight, proposedheight);
            }
        }

}

在活动中

    public class mAct extends Activity implements  IKeyboardChanged {
    private VLinearLayoutKeyboardListener vllkl;
    onCreate(){
    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    }

    @Override
          public void onDestroy() {
            super.onDestroy();
            vllkl.removeKeyboardStateChangedListener(this);

         }

    @Override
        public void onKeyboardShown(int actualHeight, int proposedheight) {
//actualHeight - old value
//proposedheight - new value
            Log.d(TAG,"onKeyboardShown(): [" + actualHeight + ", " + proposedheight + "]");
//here you can setup Views heights
        }
    @Override
        public void onKeyboardHidden(int actualHeight, int proposedheight) {
//actualHeight - old value
//proposedheight - new value
            Log.d(TAG,"onKeyboardHidden(): [" + actualHeight + ", " + proposedheight + "]");
//here you can setup Views heights
        }
    }
XML中的

<?xml version="1.0" encoding="utf-8"?>
<your.package.name.VLinearLayoutKeyboardListener xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/black"
        android:orientation="vertical"
        android:id="@+id/root">
....
</your.package.name.VLinearLayoutKeyboardListener >