如何让软键盘覆盖一个视图,而让其他视图占用可用的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 的代码。
背景
假设我有一个视图作为背景(在我的例子中是 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 的代码。