问题
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