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

时间秒杀一切 提交于 2019-12-23 22:26:11

问题


Background

Suppose I have a view that works as the background (MapFragment in my case), and some other views that are the content of the activity.

The problem

Focusing an EditText, it shows the soft keyboard, causing all views to change their sizes, including the background view.

This means that showing the soft keyboard causes the background to "jump" and re-layout itself.

This is especially problematic in my case, as I use MapFragment as the background. That's because I can think of a workaround to detect the keyboard size, and show only the upper area of the background view, but there is little control of the MapFragment in how it looks and work.

Here's a demo of the problem:

Here's a sample xml, used inside the sample of the 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>

What I've found

I know that we can set the "windowSoftInputMode" flag in the manifest, but this is an all-or-nothing solution. It affects all of the views, and not a single one.

The question

How can I let the soft keyboard change the layout of all views except specific ones?

Is it even possible?


EDIT: looking at "gio"'s solution, it works. Here's a more optimized way to do it:

    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();
        }
    });

回答1:


It's possible according your initial requirements. Idea is to change bottom margin of needed view. Value will be calculated as difference between shown and real height of root view. I wrapped TextView into FrameLayout for case if you need to control more views there.

xml layout

<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));
}

}

Result:




回答2:


This is a little bit off topic from what you guys have been discussing, but I had the same issue with the Google Map layout being re adjusted when user clicks on an EditText on that fragment layout. My problem was that the GoogleMap was a fragment of another view (ViewPager). In order to fix it, I had to programmatically add getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) to the ViewPager Class.

Layout Hierarchy: Activity --> ViewPager --> GoogleMap Fragment. Code added to ViewPager on the onCreate method.



来源:https://stackoverflow.com/questions/34851155/how-to-have-the-soft-keyboard-cover-a-view-yet-cause-other-views-to-take-the-av

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!