如何让软键盘覆盖一个视图,而让其他视图占用可用的space?

How to have the soft keyboard cover a view, yet cause other views to take the available space?

背景

假设我有一个视图作为背景(在我的例子中是 MapFragment),还有一些其他视图是 activity.

的内容

问题

聚焦 EditText,它显示软键盘,导致所有视图更改其大小,包括背景视图。

这意味着显示软键盘会导致背景 "jump" 并自行重新布局。

这对我来说尤其成问题,因为我使用 MapFragment 作为背景。那是因为我能想到一种解决方法来检测键盘大小,并仅显示背景视图的上部区域,但在 MapFragment 的外观和工作方式方面几乎没有控制权。

这是问题的演示:

这是一个示例 xml,在 mapFragment 的示例中使用:

basic_demo.xml

<FrameLayout
    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">

    <fragment
        android:id="@+id/map"
        class="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <EditText
        android:id="@android:id/edit"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="20dp"
        android:background="#66ffffff"
        android:digits="0123456789()-+*"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:gravity="center_vertical|left"
        android:imeOptions="actionSearch|flagNoExtractUi"
        android:inputType="phone"
        android:singleLine="true"
        android:textColorHint="#aaa"
        android:textSize="18dp"
        tools:hint="Search Phones ..."/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginBottom="30dp"
        android:background="#66ffffff"
        android:text="This should always be shown, at the bottom"/>
</FrameLayout>

我发现了什么

我知道我们可以在清单中设置“windowSoftInputMode”标志,但这是一个孤注一掷的解决方案。它会影响所有视图,而不是单个视图。

问题

如何让软键盘改变除特定视图之外的所有视图的布局?

有可能吗?


编辑:查看 "gio" 的解决方案,它有效。这是一个更优化的方法:

    View view=...
    mContainerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        final Rect outGlobalRect = new Rect(), outWindowRect = new Rect();

        @Override
        public void onGlobalLayout() {
            mContainerView.getGlobalVisibleRect(outGlobalRect);
            mContainerView.getWindowVisibleDisplayFrame(outWindowRect);
            int marginBottom = outGlobalRect.bottom - outWindowRect.bottom;
            final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams();
            if (layoutParams.bottomMargin == marginBottom)
                return;
            layoutParams.setMargins(0, 0, 0, marginBottom);
            view.requestLayout();
        }
    });

根据您的初步要求是可以的。想法是改变所需视图的底部边距。值将计算为根视图的显示高度与实际高度之间的差异。如果您需要在那里控制更多视图,我将 TextView 包装到 FrameLayout 中。

xml布局

<FrameLayout
android:id="@+id/ac_mp_container"
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">

<fragment
    android:id="@+id/ac_mp_map"
    android:name="com.google.android.gms.maps.SupportMapFragment"

    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tivogi.so.MapsActivity" />

<EditText
    android:layout_width="100dp"
    android:layout_height="wrap_content" />

<FrameLayout
    android:id="@+id/ac_mp_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom"
    android:padding="16dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This should always be shown, at the bottom" />

</FrameLayout>

activity class

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

private View mContainerView;
private GoogleMap mMap;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);
    // Obtain the SupportMapFragment and get notified when the map is ready to be used.
    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
            .findFragmentById(R.id.ac_mp_map);
    mapFragment.getMapAsync(this);
    mContainerView = findViewById(R.id.ac_mp_container);
    mContainerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            View view = findViewById(R.id.ac_mp_view);
            int marginBottom;
            Rect outGlobalRect = new Rect();
            Rect outWindowRect = new Rect();
            DisplayMetrics metrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(metrics);
            mContainerView.getGlobalVisibleRect(outGlobalRect);
            mContainerView.getWindowVisibleDisplayFrame(outWindowRect);
            marginBottom = outGlobalRect.bottom - outWindowRect.bottom;
            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM);
            layoutParams.setMargins(0, 0, 0, marginBottom);
            view.setLayoutParams(layoutParams);
        }
    });

}

/**
 * Manipulates the map once available.
 * This callback is triggered when the map is ready to be used.
 * This is where we can add markers or lines, add listeners or move the camera. In this case,
 * we just add a marker near Sydney, Australia.
 * If Google Play services is not installed on the device, the user will be prompted to install
 * it inside the SupportMapFragment. This method will only be triggered once the user has
 * installed Google Play services and returned to the app.
 */
@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;

    // Add a marker in Sydney and move the camera
    LatLng sydney = new LatLng(-34, 151);
    mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
    mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}

}

结果:

这与你们一直在讨论的内容有点偏离主题,但我遇到了同样的问题,当用户单击该片段布局上的 EditText 时,Google 地图布局被重新调整。我的问题是 GoogleMap 是另一个视图 (ViewPager) 的片段。为了修复它,我不得不以编程方式将 getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) 添加到 ViewPager Class.

布局层次结构:Activity --> ViewPager --> Google地图片段。在 onCreate 方法上添加到 ViewPager 的代码。