多数Android平板老项目初始仅支持横屏展示,在业务需求升级、设备适配多元化的场景下,需要兼容竖屏显示效果。常规适配方式需重构布局文件,改造成本高、易引发兼容bug。本文记录一种零布局修改、纯代码动态适配的横竖屏解决方案,通过配置变更监听,动态修改网格列表列数、Item间距、文本展示字数及分割线状态,无需重建页面,即可实现平板横竖屏无缝切换适配,适配效率高、稳定性强,可快速复用至各类列表业务页面。
步骤如下
1、在AndroidManifest.xml中activity的属性设置
ini
android:screenOrientation="unspecified"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
属性释义:
unspecified:跟随系统屏幕方向,自动适配横竖屏,不固定页面展示方向。configChanges多属性组合:拦截屏幕方向、尺寸、布局变化触发的Activity重建,仅回调onConfigurationChanged方法,实现无刷新适配。
2、activity/Fragment 中的配置
以列表页为例,横屏时可以每行显示3个item,切换竖屏只能显示2个且item之间的间距也会变化,item中文字显示的最大字符限制也不同,通过在onConfigurationChanged中重新设置列表无需新增或变动布局文件。
ini
private boolean isLandscape; // 屏幕是否为横屏
private int spanCount = 2; // 列数
private GridSpacingItemDecoration decoration;
private int itemDecoration = AppUtil.getDimension(R.dimen.y30); //间距
@Override
protected void initView(View view) {
//方向配置
isLandscape = BaseUtil.getOrientation();
if(isLandscape){
spanCount = 3;
itemDecoration = AppUtil.getDimension(R.dimen.y90);
}
decoration = new GridSpacingItemDecoration(spanCount, itemDecoration, false);
binding.rvList.addItemDecoration(decoration);
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), spanCount);
binding.rvList.setLayoutManager(layoutManager);
adapter.setLandscape(isLandscape); //设置方向
adapter.setSpanCount(spanCount); //设置列数
binding.rvList.setAdapter(adapter);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
binding.rvList.removeItemDecoration(decoration); //先移除上次设置,不然会叠加影响
isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
if(isLandscape){
spanCount = 3;
itemDecoration = AppUtil.getDimension(R.dimen.y90);
}else{
spanCount = 2;
itemDecoration = AppUtil.getDimension(R.dimen.y90);
}
decoration = new GridSpacingItemDecoration(spanCount, itemDecoration, false);
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), spanCount);
binding.rvList.setLayoutManager(layoutManager);
binding.rvList.addItemDecoration(decoration);
if(adapter!=null){ //刷新
adapter.setLandscape(isLandscape);
adapter.setSpanCount(spanCount);
UIHandler.getHandler().postDelayed(() -> adapter.notifyDataSetChanged(),100);
}
}
public static int getDimension(int id) {
return (int) BaseApplication.getInstance().getResources().getDimension(id);
}
/**
* 获取屏幕方向
*/
public static boolean getOrientation() {
Configuration mConfiguration = BaseApplication.getInstance().getResources().getConfiguration(); //获取设置的配置信息
//横屏
return mConfiguration.orientation == Configuration.ORIENTATION_LANDSCAPE;
}
3、adapter中设置
ini
private int spanCount = 2;
private boolean isLandscape = false; // 是否横屏
public void setSpanCount(int spanCount) {
this.spanCount = spanCount;
}
public void setLandscape(boolean isLandscape) {
this.isLandscape = isLandscape;
}
@Override
protected void convert(@NotNull DataBindBaseViewHolder baseViewHolder, XXBean bean) {
ItemxxBinding binding = (ItemxxBinding) baseViewHolder.getDataBinding();
int position = baseViewHolder.getLayoutPosition();
int size = getData().size();
int lastRowCount = 0;
if(size > 0){
lastRowCount = size % spanCount;
}
if (lastRowCount == 0) {
lastRowCount = spanCount;
}
int firstItemOfLastRow = size - lastRowCount;
// 如果是最后一排,隐藏底线view;否则显示底线view
if (position >= firstItemOfLastRow) {
binding.view.setVisibility(View.GONE);
} else {
binding.view.setVisibility(View.VISIBLE);
}
String desc = bean.getDesc();
String name = bean.getAppName();
int numDesc = 11;
int numName = 7;
if(isLandscape){
numDesc = 20;
numName = 10;
}
if (desc != null && desc.length() > numDesc) {
desc = desc.substring(0, numDesc) + "...";
}
if (name != null && name.length() > numName) {
name = name.substring(0, numName) + "...";
}
binding.tvDesc.setText(desc);
binding.tvName.setText(name);
}
1. 为什么要加configChanges配置?
默认情况下,屏幕旋转会触发Activity销毁重建,会导致列表滚动位置重置、页面闪烁、临时数据丢失。配置对应参数后,系统不会重建页面,仅回调onConfigurationChanged,可以手动精准适配UI,体验更流畅。
2. 为什么必须移除旧的ItemDecoration?
每次旋转屏幕都会新增一个间距装饰器,若不主动移除旧装饰器,会出现间距叠加、Item间距越来越大的UI bug,是横竖屏列表适配的高频踩坑点。
3. 为什么需要延迟刷新适配器?
屏幕旋转瞬间,视图布局尚未完成绘制,立即调用notifyDataSetChanged可能刷新失效。100ms轻微延迟可保证布局加载完成后再刷新UI,适配更稳定。
方案优势:低侵入、高稳定、全覆盖、易复用