Manifest配置android:supportsRtl=false时如何运行时修改ConstraintLayout布局方向
问题1解答
你遇到的前两个控件生效、第三个没有变化的原因非常简单:你的第三个TextView配置的是app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent",同时宽度设为0px(也就是MATCH_CONSTRAINT),不管是LTR还是RTL模式下,它都会占满父布局的整个宽度,边界没有变化,所以肉眼看不到方向切换的效果。
你只要给这个TextView加上android:gravity="start"属性,切换方向的时候就能看到文字会自动从左对齐变成右对齐,验证方向切换已经生效。
想要所有控件都响应方向调整,只要保证所有布局约束都用start/end代替left/right,涉及文字、内容对齐的属性都用start代替left、end代替right即可。
问题2解答
Manifest全局配置android:supportsRtl="false"的前提下,要单独让指定ConstraintLayout支持RTL,可以用下面两个方案,都不会影响其他模块:
方案1:自定义ConstraintLayout强制覆盖布局方向逻辑
重写ConstraintLayout的resolveLayoutDirection方法,跳过系统全局supportsRtl的判断,强制使用你设置的方向:
class RtlCapableConstraintLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { var forceLayoutDirection: Int? = null override fun resolveLayoutDirection(): Int { forceLayoutDirection?.let { if (isLayoutDirectionResolved && layoutDirection == it) return it setLayoutDirection(it) return it } return super.resolveLayoutDirection() } }
使用的时候直接替换布局里的ConstraintLayout为这个自定义类,切换方向时调用:
container.forceLayoutDirection = View.LAYOUT_DIRECTION_RTL container.requestLayout()
方案2:用独立主题inflater加载布局
给要加载的布局单独套一个开启了supportsRtl的主题,完全隔离全局配置:
- 先在
res/values/styles.xml里加一个独立主题:
<style name="RtlEnabledTheme" parent="AppTheme"> <item name="android:supportsRtl">true</item> </style>
- 加载布局的时候用套了这个主题的Context来inflate:
val rtlContext = ContextThemeWrapper(baseContext, R.style.RtlEnabledTheme) val inflater = LayoutInflater.from(rtlContext) val container = inflater.inflate(R.layout.your_layout, null) as ConstraintLayout // 之后正常设置layoutDirection即可生效 container.layoutDirection = View.LAYOUT_DIRECTION_RTL
如果是悬浮窗场景,创建WindowManager.LayoutParams的时候用这个rtlContext即可,不会影响其他页面逻辑。
内容的提问来源于stack exchange,提问作者android developer

