Android纯代码实现下如何高效处理屏幕方向等配置变更
高效处理Android配置变更的方案
针对纯代码编写的Android布局(非Jetpack Compose),推荐采用Android官方首选的「让系统自动重建组件+资源适配」方案,替代手动修改onConfigurationChanged的繁琐逻辑,具体实现方式如下:
方案1:利用资源限定符自动适配布局逻辑
通过Android资源系统的限定符(如land、sw600dp等),在不同配置下加载不同的布局参数,代码无需手动判断配置类型,由系统自动匹配资源。
步骤示例:
定义自定义布局属性
在res/values/attrs.xml中创建用于控制布局的属性:<declare-styleable name="OrientationLayoutConfig"> <attr name="editTextWidth" format="dimension" /> <attr name="editTextAlignRule" format="integer" /> <attr name="buttonRelativeRule" format="integer" /> </declare-styleable>分配置编写资源
- 竖屏配置(
res/values/styles.xml):<style name="LayoutStyle"> <item name="editTextWidth">match_parent</item> <item name="editTextAlignRule">@integer/rule_align_parent_top</item> <item name="buttonRelativeRule">@integer/rule_below_edittext</item> </style> - 横屏配置(
res/values-land/styles.xml):<style name="LayoutStyle"> <item name="editTextWidth">180dp</item> <item name="editTextAlignRule">@integer/rule_align_parent_left</item> <item name="buttonRelativeRule">@integer/rule_to_right_of_edittext</item> </style>
(注:需在
res/values/integers.xml中定义对应规则常量,如rule_align_parent_top对应RelativeLayout.ALIGN_PARENT_TOP的整数值)- 竖屏配置(
代码中读取资源并构建布局
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(buildLayout()) } private fun buildLayout(): View { val attrs = obtainStyledAttributes(R.style.LayoutStyle, R.styleable.OrientationLayoutConfig) // 从资源中读取布局参数 val editTextWidth = attrs.getDimensionPixelSize( R.styleable.OrientationLayoutConfig_editTextWidth, ViewGroup.LayoutParams.MATCH_PARENT ) val editTextAlignRule = attrs.getInt( R.styleable.OrientationLayoutConfig_editTextAlignRule, RelativeLayout.ALIGN_PARENT_TOP ) val buttonRelativeRule = attrs.getInt( R.styleable.OrientationLayoutConfig_buttonRelativeRule, RelativeLayout.BELOW ) attrs.recycle() // 创建EditText val editText = EditText(this).apply { id = R.id.et_input // 设置唯一ID,让系统自动保存输入状态 layoutParams = RelativeLayout.LayoutParams(editTextWidth, ViewGroup.LayoutParams.WRAP_CONTENT).apply { addRule(editTextAlignRule) } } // 创建Button val button = Button(this).apply { text = "提交" layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { addRule(buttonRelativeRule, editText.id) // 根据规则补充额外布局属性 if (buttonRelativeRule == RelativeLayout.BELOW) { addRule(RelativeLayout.ALIGN_PARENT_LEFT) } else { addRule(RelativeLayout.CENTER_VERTICAL) marginStart = 16.dpToPx() } } } // 构建根布局 return RelativeLayout(this).apply { addView(editText) addView(button) } } // 扩展函数:dp转px private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).toInt()
方案2:拆分布局创建逻辑,根据配置自动调用
将不同配置下的布局创建逻辑拆分为独立方法,在组件创建时根据当前配置自动选择执行,系统重建时会自动适配新配置。
代码示例:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(createLayoutByOrientation()) } private fun createLayoutByOrientation(): View { return if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { createLandscapeLayout() } else { createPortraitLayout() } } // 竖屏布局逻辑 private fun createPortraitLayout(): View { val editText = EditText(this).apply { id = R.id.et_input layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { addRule(RelativeLayout.ALIGN_PARENT_TOP) setMargins(16.dpToPx(), 16.dpToPx(), 16.dpToPx(), 0) } } val button = Button(this).apply { text = "提交" layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { addRule(RelativeLayout.BELOW, editText.id) addRule(RelativeLayout.ALIGN_PARENT_LEFT) setMargins(16.dpToPx(), 16.dpToPx(), 0, 0) } } return RelativeLayout(this).apply { addView(editText); addView(button) } } // 横屏布局逻辑 private fun createLandscapeLayout(): View { val editText = EditText(this).apply { id = R.id.et_input layoutParams = RelativeLayout.LayoutParams(200.dpToPx(), ViewGroup.LayoutParams.WRAP_CONTENT).apply { addRule(RelativeLayout.ALIGN_PARENT_LEFT) addRule(RelativeLayout.CENTER_VERTICAL) setMargins(16.dpToPx(), 0, 0, 0) } } val button = Button(this).apply { text = "提交" layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { addRule(RelativeLayout.TO_RIGHT_OF, editText.id) addRule(RelativeLayout.CENTER_VERTICAL) setMargins(16.dpToPx(), 0, 0, 0) } } return RelativeLayout(this).apply { addView(editText); addView(button) } } private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).toInt()
关键注意点:状态自动恢复
- 所有需要保留状态的View(如EditText)必须设置唯一ID,系统会自动保存其输入内容、选中状态等。
- 若有自定义状态(如按钮点击计数),可通过重写
onSaveInstanceState和onRestoreInstanceState实现:private var clickCount = 0 override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putInt("click_count", clickCount) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) clickCount = savedInstanceState.getInt("click_count", 0) }
内容的提问来源于stack exchange,提问作者Jatin guglani
相关产品推荐
相关产品推荐

