【问题标题】:Adjust Scrollview height when keyboard is hidden/visible键盘隐藏/可见时调整滚动视图高度
【发布时间】:2016-01-31 16:32:43
【问题描述】:

我有一个带有一堆控件(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 框架不知道正在显示此键盘,因此滚动视图不会调整。控件仍然隐藏在自定义键盘后面。因此,我在自定义键盘中实现了一个回调,该回调在键盘可见时被调用。

// 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的计算就搞砸了,因为现在master的相对布局高度很小(总高度-自定义键盘高度-默认键盘高度)。所以上面的scrollview高度计算是错误的。滚动视图现在被限制在屏幕顶部的一个非常小的区域,然后是空白区域,然后是底部的自定义键盘。我通过在 EditText 的触摸侦听器中添加对 AdjustScrollView 的调用来解决此问题(因此,无论何时发生这种情况,用户都可以单击该小视图,并且触摸侦听器将调整滚动视图)。这种解决方法是不可取的,因为它会使长按无用(我希望用户能够长按以显示剪切/复制/粘贴等默认系统菜单)。需要额外触摸的解决方法使这个系统菜单消失了。

问题2:如果自定义键盘被隐藏,滚动视图不会再次占用整个屏幕大小。被自定义键盘占据的屏幕底部保持空白,滚动视图仅在屏幕顶部。

【问题讨论】:

  • 你试过 android:windowSoftInputMode="adjustPan"

标签: android keyboard scrollview


【解决方案1】:

我找到的唯一解决方案是使用键盘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 >

【讨论】:

    猜你喜欢
    • 2013-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-22
    • 1970-01-01
    相关资源
    最近更新 更多