一聚教程网:一个值得你收藏的教程网站

热门教程

Android开发: 拒绝 Activity 重建!onConfigurationChanged 达成平板横竖屏无缝切换

时间:2026-06-03 09:15:01 编辑:袖梨 来源:一聚教程网

针对现有Android平板老项目仅支持横屏的局限性,本文提出一种无需修改布局文件的横竖屏切换方案,通过onConfigurationChanged动态调整列表列数、间距及文本显示,实现零刷新无缝适配。

Android开发: 拒绝 Activity 重建!onConfigurationChanged 实现平板横竖屏无缝切换

零布局修改、纯代码动态适配的横竖屏解决方案,通过配置变更,动态修改网格列表列数、Item间距、文本展示字数及分割线状态,无需重建页面,即可实现平板横竖屏无缝切换,适配效率高、稳定性强,可快速复用至各类列表业务页面。

步骤如下

1、在AndroidManifest.xml中activity的属性设置

android:screenOrientation="unspecified"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"

属性释义

  1. unspecified:跟随系统屏幕方向,自动适配横竖屏,不固定页面展示方向。
  2. configChanges 多属性组合:拦截屏幕方向、尺寸、布局变化触发的Activity重建,仅回调onConfigurationChanged方法,实现无刷新适配。

2、activity/Fragment 中的配置

以列表页为例,横屏时可以每行显示3个item,切换竖屏只能显示2个且item之间的间距也会变化,item中文字显示的最大字符限制也不同,通过在onConfigurationChanged中重新设置列表无需新增或变动布局文件。

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中设置

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,适配更稳定。

本方案凭借低侵入性、高稳定性,可覆盖各类列表页面,易于复用,是平板横竖屏适配的实用选择。

热门栏目