Android:如何在软键盘出现时只调整视图的部分大小

58
我有一个视图,上面放置了一个EditText字段和一个ImageView。当键盘弹出时,我希望窗口重新调整大小,以便EditText不再被键盘隐藏。在AndroidManifest文件中,我声明了android:windowSoftInputMode="adjustResize",屏幕会被调整大小,但问题是我希望ImageView不被调整大小。 如何使ImageView不受影响? 我是否可以填充只包含ImageView的附加布局,还是调整大小仍将影响它?

你能为此发布屏幕截图吗? - Digvesh Patel
我认为每次键盘出现时,我都需要对图像进行缩放或裁剪。 - pdfj
你解决了吗? - Digvesh Patel
8个回答

97

完整的解决方案涉及几个关键点:

  • 使用RelativeLayout,以便可以设置Views重叠在一起
  • 使用android:layout_alignParentBottom="true"EditTextWindows底部对齐
  • 在您的清单中使用android:windowSoftInputMode="adjustResize",这样当键盘弹出时,Window的底部会发生变化(如您所提到的)
  • ImageView放置在ScrollView内,以便ImageView可以比Window更大,并使用ScrollView#setEnabled(false)禁用ScrollView上的滚动

这是布局文件:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.so3.MainActivity">
    <ScrollView
        android:id="@+id/scroll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <ImageView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:src="@drawable/stickfigures"/>
    </ScrollView>
    <EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@android:color/holo_blue_bright"
        android:text="Please enter text"
        android:textSize="40sp"
        android:gravity="center_horizontal"/>
</RelativeLayout>

这是我的活动

package com.so3;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ScrollView;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ScrollView sv = (ScrollView)findViewById(R.id.scroll);
        sv.setEnabled(false);
    }
}

我的Android清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.so3" >
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.so3.MainActivity"
            android:windowSoftInputMode="adjustResize"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

我的解决方案的屏幕截图

screenshot 1 screenshot 2


2
把它变成滚动视图并禁用滚动是我遗漏的部分。明天我会尝试实现它。 - pdfj
1
@BrianAttwell Fragment怎么办? - Pratik Butani
1
+1。很好的解决方案,但ScrollView的setEnabled(false)对我不起作用。我使用了ScrollView的包装器来禁用滚动https://dev59.com/VG025IYBdhLWcg3w25uy#5763815 - valerybodak
在ScrollView中添加ImageView就像魔术一样。谢谢。 - B.shruti
4
如果情况是width='match_parent'且图像很小,那么imageView就不会占满整个屏幕,为了让它占满整个屏幕,添加一个fillViewport的scrollView又会使图像在软键盘弹出时重新调整大小,请帮忙解决。 - B.shruti
显示剩余5条评论

3

我避免了使用ScrollView,因为它会使我的图片可以滚动,而我不想要这个功能。相反,我使用了samples-keyboardheight 计算器和onKeyboardHeightChanged 方法来重新计算底部Edittext的位置,并将其放置在键盘之上。我在Manifest文件中使用了一个标志。

android:windowSoftInputMode="adjustNothing|stateHidden"

这里是 KeyboardHeightProvider
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager.LayoutParams;
import android.widget.PopupWindow;
/**
 * The keyboard height provider, this class uses a PopupWindow
 * to calculate the window height when the floating keyboard is opened and closed. 
 */
public class KeyboardHeightProvider extends PopupWindow {

    /** The tag for logging purposes */
    private final static String TAG = "sample_KeyboardHeightProvider";

    /** The keyboard height observer */
    private KeyboardHeightObserver observer;

    /** The cached landscape height of the keyboard */
    private int keyboardLandscapeHeight;

    /** The cached portrait height of the keyboard */
    private int keyboardPortraitHeight;

    /** The view that is used to calculate the keyboard height */
    private View popupView;

    /** The parent view */
    private View parentView;

    /** The root activity that uses this KeyboardHeightProvider */
    private Activity activity;

    /** 
     * Construct a new KeyboardHeightProvider
     * 
     * @param activity The parent activity
     */
    public KeyboardHeightProvider(Activity activity) {
        super(activity);
        this.activity = activity;

        LayoutInflater inflator = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        this.popupView = inflator.inflate(R.layout.popupwindow, null, false);
        setContentView(popupView);

        setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);

        parentView = activity.findViewById(android.R.id.content);

        setWidth(0);
        setHeight(LayoutParams.MATCH_PARENT);

        popupView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

                @Override
                public void onGlobalLayout() {
                    if (popupView != null) {
                        handleOnGlobalLayout();
                    }
                }
            });
    }

    /**
     * Start the KeyboardHeightProvider, this must be called after the onResume of the Activity.
     * PopupWindows are not allowed to be registered before the onResume has finished
     * of the Activity.
     */
    public void start() {

        if (!isShowing() && parentView.getWindowToken() != null) {
            setBackgroundDrawable(new ColorDrawable(0));
            showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
        }
    }

    /**
     * Close the keyboard height provider, 
     * this provider will not be used anymore.
     */
    public void close() {
        this.observer = null;
        dismiss();
    }

    /** 
     * Set the keyboard height observer to this provider. The 
     * observer will be notified when the keyboard height has changed. 
     * For example when the keyboard is opened or closed.
     * 
     * @param observer The observer to be added to this provider.
     */
    public void setKeyboardHeightObserver(KeyboardHeightObserver observer) {
        this.observer = observer;
    }

    /**
     * Get the screen orientation
     *
     * @return the screen orientation
     */
    private int getScreenOrientation() {
        return activity.getResources().getConfiguration().orientation;
    }

    /**
     * Popup window itself is as big as the window of the Activity. 
     * The keyboard can then be calculated by extracting the popup view bottom 
     * from the activity window height. 
     */
    private void handleOnGlobalLayout() {

        Point screenSize = new Point();
        activity.getWindowManager().getDefaultDisplay().getSize(screenSize);

        Rect rect = new Rect();
        popupView.getWindowVisibleDisplayFrame(rect);

        // REMIND, you may like to change this using the fullscreen size of the phone
        // and also using the status bar and navigation bar heights of the phone to calculate
        // the keyboard height. But this worked fine on a Nexus.
        int orientation = getScreenOrientation();
        int keyboardHeight = screenSize.y - rect.bottom;

        if (keyboardHeight == 0) {
            notifyKeyboardHeightChanged(0, orientation);
        }
        else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            this.keyboardPortraitHeight = keyboardHeight; 
            notifyKeyboardHeightChanged(keyboardPortraitHeight, orientation);
        } 
        else {
            this.keyboardLandscapeHeight = keyboardHeight; 
            notifyKeyboardHeightChanged(keyboardLandscapeHeight, orientation);
        }
    }

    /**
     *
     */
    private void notifyKeyboardHeightChanged(int height, int orientation) {
        if (observer != null) {
            observer.onKeyboardHeightChanged(height, orientation);
        }
    }

    public interface KeyboardHeightObserver {
        void onKeyboardHeightChanged(int height, int orientation);
    }
}

popupwindow.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/popuplayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:orientation="horizontal"/>

这里是 MainActivity.java 文件:

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;

public class MainActivity extends AppCompatActivity  implements KeyboardHeightProvider.KeyboardHeightObserver {

    private KeyboardHeightProvider keyboardHeightProvider;

    private ViewGroup relativeView;
    private float initialY;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        keyboardHeightProvider = new KeyboardHeightProvider(this);

        relativeView = findViewById(R.id.bottomEditor);
        relativeView.post(() -> initialY = relativeView.getY());

        View view = findViewById(R.id.activitylayout);
        view.post(() -> keyboardHeightProvider.start());


    }

    @Override
    public void onKeyboardHeightChanged(int height, int orientation) {
        if(height == 0){
            relativeView.setY(initialY);
            relativeView.requestLayout();
        }else {

            float newPosition = initialY - height;
            relativeView.setY(newPosition);
            relativeView.requestLayout();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        keyboardHeightProvider.setKeyboardHeightObserver(null);
    }

    @Override
    public void onResume() {
        super.onResume();
        keyboardHeightProvider.setKeyboardHeightObserver(this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        keyboardHeightProvider.close();
    }
}

activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activitylayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        />

    <RelativeLayout
        android:id="@+id/bottomEditor"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        >

        <EditText
            android:id="@+id/edit_message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            android:layout_toStartOf="@+id/btn_send"
            android:hint="Add caption"
            android:paddingBottom="12dp"
            android:paddingLeft="8dp"
            android:paddingRight="8dp"
            android:paddingStart="8dp"
            android:paddingTop="12dp"
            />

        <ImageButton
            android:id="@+id/btn_send"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_alignBottom="@+id/edit_message"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_marginEnd="4dp"
            android:layout_marginRight="4dp"
            app:srcCompat="@android:drawable/ic_menu_send"
            />

    </RelativeLayout>
</RelativeLayout>

P.S.:键盘高度计算代码来自siebeprojects

这里是实现的演示应用程序示例。


1
谢谢,我不能在全屏活动上使用这个,但我对您的代码进行了一些调整,现在可以用了 :)。 - htafoya
你可以发布那段代码,这样其他人可能会发现它很有用。 :) - Nilesh Deokar

1
final View activityRootView = findViewById(R.id.mainScroll);

activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                int heightView = activityRootView.getHeight();
                int widthView = activityRootView.getWidth();
                if (1.0 * widthView / heightView > 1) {

                    Log.d("keyboarddddd      visible", "no");
                    relativeLayoutForImage.setVisibility(View.GONE);
                    relativeLayoutStatic.setVisibility(View.GONE);
                    //Make changes for Keyboard not visible


                } else {

                    Log.d("keyboarddddd      visible ", "yes");

                    relativeLayoutForImage.setVisibility(View.VISIBLE);
                    relativeLayoutStatic.setVisibility(View.VISIBLE);
                    //Make changes for keyboard visible


                }
            }
        });

这段代码确实有效,我使用这段代码解决了类似的问题,请尝试使用这段代码。它对你非常有帮助。 - miral patel
嗨,miral,我的布局中有一个可编辑文本框,可以通过触摸移动。我的问题是,当我将编辑文本框移动到屏幕顶部并打开键盘时,编辑文本框不可见了,它被推到了屏幕外面。但是我希望在打开键盘时,编辑文本框能够出现在键盘的上方。你有什么想法吗?谢谢。 - Patel Hiren

0
最佳解决方案是使用DialogFragment。
显示对话框。
DialogFragment.show(getSupportFragmentManager(), DialogFragment.TAG);

全屏

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    Dialog dialog = new Dialog(getActivity(), R.style.MainDialog) { //set the style, the best code here or with me, we do not change
        @Override
        public void onBackPressed() {
            super.onBackPressed();
            getActivity().finish();
        }
    };
    return dialog;
}

样式

<style name="MainDialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">false</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:background">@null</item>
        <item name="android:windowAnimationStyle">@null</item>
    </style>

布局活动

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

布局对话框片段

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/transparent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentStart="true"
        android:background="@color/background_transparent_60"
        android:gravity="center_vertical">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/spacing_1_8dp"
            android:layout_marginLeft="@dimen/spacing_1_8dp"
            android:layout_marginRight="@dimen/spacing_1_8dp"
            android:layout_weight="1"
            android:hint="@string/comment_entry_hint"
            android:inputType="textMultiLine"
            android:maxLines="4"
            android:textColor="@color/white"
            android:textColorHint="@color/secondary_text_hint"
            android:textSize="@dimen/text_2_12sp" />

        <ImageView
            android:layout_width="@dimen/livestream_comment_height"
            android:layout_height="@dimen/livestream_comment_height"
            android:layout_margin="@dimen/spacing_1_8dp"
            android:src="@drawable/ic_send" />

    </LinearLayout>

</RelativeLayout>

0
对我有效的解决方案是在AndroidManifest.xml文件中,在那个activity标签中加入


android:windowSoftInputMode="stateHidden|adjustResize|adjustNothing"

一切就绪...希望这对你有用。


0
在我看来,最简单的方法是这两个更改的组合
android:windowSoftInputMode="adjustResize"

在你的 AndroidManifest.xml 文件中

+

getWindow().setBackgroundDrawable(your_image_drawable);

在你的活动中,在@onCreate()方法中

这对我有效。


有没有一种方法可以使用这种方法,同时保持图像的纵横比? - jk7

0

对我来说,我不想假设键盘的高度是固定的。无论你关心哪个视图,请创建一个onTouchListener,然后执行以下操作:

    setOnTouchListener(new OnTouchListener()  {



        Runnable shifter=new Runnable(){
            public void run(){
                try {
                    int[] loc = new int[2];                 
                    //get the location of someview which gets stored in loc array
                    findViewById(R.id.someview).getLocationInWindow(loc);
                    //shift so user can see someview
                    myscrollView.scrollTo(loc[0], loc[1]);   
                }
                catch (Exception e) {
                    e.printStackTrace();
                }   
            }}
        };

        Rect scrollBounds = new Rect();
        View divider=findViewById(R.id.someview);
        myscollView.getHitRect(scrollBounds);
        if (!divider.getLocalVisibleRect(scrollBounds))  {
            // the divider view is NOT  within the visible scroll window thus we need to scroll a bit.
            myscollView.postDelayed(shifter, 500);
        }



    });

//本质上,我们创建了一个可运行的程序,它会滚动到某个视图的新位置,该视图是您希望在屏幕上可见的。只有当该视图不在滚动视图的边界内(即不在屏幕上)时,才执行该可运行程序。这样,它就会将滚动视图移动到所引用的视图(在我的情况下是“someview”,它是一条分隔线)。


这段代码在第一次点击时不会被执行,在我的情况下,需要第二次点击才能生效。 - User3
可能是您注册ontouchlistener的方式不对,或者有一个正在消耗触摸事件的视图焦点更改监听器等。我需要看到代码。 - j2emanue

-9
    final View activityRootView = findViewById(R.id.mainScroll);

    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
            new OnGlobalLayoutListener() {


                @Override
                public void onGlobalLayout() {
                    int heightView = activityRootView.getHeight();
                    int widthView = activityRootView.getWidth();
                    if (1.0 * widthView / heightView > 1) {

                        Log.d("keyboarddddd      visible", "no");
                        relativeLayoutForImage.setVisibility(View.GONE);
                        relativeLayoutStatic.setVisibility(View.GONE);
                        //Make changes for Keyboard not visible
                        //relativeLayoutForImage.setVisibility(View.VISIBLE);
                        //relativeLayoutStatic.setVisibility(View.VISIBLE);

                    } else {

                        Log.d("keyboarddddd      visible ", "yes");

                        relativeLayoutForImage.setVisibility(View.VISIBLE);
                        relativeLayoutStatic.setVisibility(View.VISIBLE);
                        //Make changes for keyboard visible

                    //  relativeLayoutForImage.setVisibility(View.GONE);
                        //relativeLayoutStatic.setVisibility(View.GONE);
                    }
                }
            });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接