and*_*per 6 android locale android-layout right-to-left left-to-right
可以使用以下方法获取当前的语言环境方向:
val isRtl=TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
Run Code Online (Sandbox Code Playgroud)
如果开发人员设置了它,也可以获得视图的布局方向:
val layoutDirection = ViewCompat.getLayoutDirection(someView)
Run Code Online (Sandbox Code Playgroud)
视图的默认layoutDirection不基于其语言环境.它实际上是LAYOUT_DIRECTION_LTR.
当您将设备的语言环境从LTR(从左到右)语言环境(如英语)更改为RTL(从右到左)语言环境(如阿拉伯语或希伯来语)时,视图将相应地对齐,但值为默认获取视图将保留LTR ...
这意味着在给定视图的情况下,我看不出如何确定它将采用的正确方向.
我做了一个简单的POC.它有一个带有TextView的LinearLayout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/linearLayout" 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" android:gravity="center_vertical" tools:context=".MainActivity">
<TextView
android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Hello World!"/>
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)
在代码中,我编写了语言环境和视图的方向:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
Log.d("AppLog", "locale direction:isRTL? $isRtl")
Log.d("AppLog", "linearLayout direction:${layoutDirectionValueToStr(ViewCompat.getLayoutDirection(linearLayout))}")
Log.d("AppLog", "textView direction:${layoutDirectionValueToStr(ViewCompat.getLayoutDirection(textView))}")
}
fun layoutDirectionValueToStr(layoutDirection: Int): String =
when (layoutDirection) {
ViewCompat.LAYOUT_DIRECTION_INHERIT -> "LAYOUT_DIRECTION_INHERIT"
ViewCompat.LAYOUT_DIRECTION_LOCALE -> "LAYOUT_DIRECTION_LOCALE"
ViewCompat.LAYOUT_DIRECTION_LTR -> "LAYOUT_DIRECTION_LTR"
ViewCompat.LAYOUT_DIRECTION_RTL -> "LAYOUT_DIRECTION_RTL"
else -> "unknown"
}
}
Run Code Online (Sandbox Code Playgroud)
结果是,即使我切换到RTL语言环境(希伯来语 - עברית),它也会在日志中打印出来:
locale direction:isRTL? true
linearLayout direction:LAYOUT_DIRECTION_LTR
textView direction:LAYOUT_DIRECTION_LTR
Run Code Online (Sandbox Code Playgroud)
当然,根据当前的语言环境,textView与正确的一侧对齐:
如果它可以像我想象的那样工作(意思是deafult的LAYOUT_DIRECTION_LOCALE),这段代码将检查视图是否在RTL中:
fun isRTL(v: View): Boolean = when (ViewCompat.getLayoutDirection(v)) {
View.LAYOUT_DIRECTION_RTL -> true
View.LAYOUT_DIRECTION_INHERIT -> isRTL(v.parent as View)
View.LAYOUT_DIRECTION_LTR -> false
View.LAYOUT_DIRECTION_LOCALE -> TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
else -> false
}
Run Code Online (Sandbox Code Playgroud)
但它不能,因为LTR是默认的,但它甚至不重要......
所以这段代码错了.
怎么可能是默认情况下,方向是LTR,但实际上它是否与右边对齐,以防语言环境发生变化?
无论开发人员为此设置(或未设置),我如何检查给定View的方向是LTR还是RTL?
怎么可能是默认情况下,方向是LTR,但实际上它是否与右边对齐,以防语言环境发生变化?
不同之处在于时间.创建视图时,会为其分配一个默认值,直到解析实际值.实际上有两个值保持:
getLayoutDirection()返回默认值LAYOUT_DIRECTION_LTR,getRawLayoutDirection()(隐藏的API)返回LAYOUT_DIRECTION_INHERIT.当原始布局方向是LAYOUT_DIRECTION_INHERIT实际布局方向时,作为measure调用的一部分被解析.然后该视图遍历其父母
ViewRootImpl).在第二种情况下,当视图层次结构尚未附加到窗口时,布局方向不会被解析并getLayoutDirection()仍然返回默认值.这是您的示例代码中发生的情况.
当视图层次结构附加到视图根时,将为该Configuration对象分配布局方向.换句话说,只有将视图层次结构附加到窗口后才能读取已解析的布局方向.
无论开发人员为此设置(或未设置),我如何检查给定View的方向是LTR还是RTL?
首先检查布局方向是否已解决.如果是,您可以使用该值.
if (ViewCompat.isLayoutDirectionResolved(view)) {
val rtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL
// Use the resolved value.
} else {
// Use one of the other options.
}
Run Code Online (Sandbox Code Playgroud)
请注意,该方法始终在Kitkat下面返回false.
如果布局方向未解决,则必须延迟检查.
选项1:将其发布到主线程消息队列.我们假设在运行时,视图层次结构已附加到窗口.
view.post {
val rtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL
// Use the resolved value.
}
Run Code Online (Sandbox Code Playgroud)
选项2:在视图层次结构准备好执行绘图时收到通知.这适用于所有API级别.
view.viewTreeObserver.addOnPreDrawListener(
object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
view.viewTreeObserver.removeOnPreDrawListener(this)
val rtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL
// Use the resolved value.
return true
}
})
Run Code Online (Sandbox Code Playgroud)
注意:您实际上可以子类化任何View并覆盖其onAttachedToWindow方法,因为布局方向作为super.onAttachedToWindow()调用的一部分被解析.其他回调(in Activity或OnWindowAttachedListener)不保证该行为,因此不要使用它们.
它从哪里获取getLayoutDirection和getRawLayoutDirection的值?
View.getRawLayoutDirection()(隐藏的API)返回您设置的内容View.setLayoutDirection().默认情况下LAYOUT_DIRECTION_INHERIT,这意味着"从我的父级继承布局方向".
View.getLayoutDirection()返回已解析的布局方向,即LOCATION_DIRECTION_LTR(也是默认值,直到实际解析)或LOCATION_DIRECTION_RTL.此方法不返回任何其他值.只有在视图是附加到视图根的视图层次结构的一部分时,返回值才有意义.
为什么LAYOUT_DIRECTION_LTR是默认值?
从历史上看,Android根本不支持从右到左的脚本(参见此处),从左到右是最明智的默认值.
视图的根会返回某个语言环境吗?
默认情况下,所有视图都继承其父级的布局方向.那么最顶层的视图在连接之前获取布局方向在哪里?无处,它不可能.
当视图层次结构附加到窗口时会发生以下情况:
final Configuration config = context.getResources().getConfiguration();
final int layoutDirection = config.getLayoutDirection();
rootView.setLayoutDirection(layoutDirection);
Run Code Online (Sandbox Code Playgroud)
使用系统区域设置设置默认配置,并从该区域设置获取布局方向.然后将根视图设置为使用该布局方向.现在它所有的孩子LAYOUT_DIRECTION_INHERIT都可以遍历并被解决到这个绝对值.
即使不需要等待视图准备就可以对我的小功能进行一些修改吗?
正如上面详细解释的那样,遗憾的是,没有.
编辑:您的小功能看起来会更像这样:
@get:RequiresApi(17)
private val getRawLayoutDirectionMethod: Method by lazy(LazyThreadSafetyMode.NONE) {
// This method didn't exist until API 17. It's hidden API.
View::class.java.getDeclaredMethod("getRawLayoutDirection")
}
val View.rawLayoutDirection: Int
@TargetApi(17) get() = when {
Build.VERSION.SDK_INT >= 17 -> {
getRawLayoutDirectionMethod.invoke(this) as Int // Use hidden API.
}
Build.VERSION.SDK_INT >= 14 -> {
layoutDirection // Until API 17 this method was hidden and returned raw value.
}
else -> ViewCompat.LAYOUT_DIRECTION_LTR // Until API 14 only LTR was a thing.
}
@Suppress("DEPRECATION")
val Configuration.layoutDirectionCompat: Int
get() = if (Build.VERSION.SDK_INT >= 17) {
layoutDirection
} else {
TextUtilsCompat.getLayoutDirectionFromLocale(locale)
}
private fun View.resolveLayoutDirection(): Int {
val rawLayoutDirection = rawLayoutDirection
return when (rawLayoutDirection) {
ViewCompat.LAYOUT_DIRECTION_LTR,
ViewCompat.LAYOUT_DIRECTION_RTL -> {
// If it's set to absolute value, return the absolute value.
rawLayoutDirection
}
ViewCompat.LAYOUT_DIRECTION_LOCALE -> {
// This mimics the behavior of View class.
TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault())
}
ViewCompat.LAYOUT_DIRECTION_INHERIT -> {
// This mimics the behavior of View and ViewRootImpl classes.
// Traverse parent views until we find an absolute value or _LOCALE.
(parent as? View)?.resolveLayoutDirection() ?: run {
// If we're not attached return the value from Configuration object.
resources.configuration.layoutDirectionCompat
}
}
else -> throw IllegalStateException()
}
}
fun View.getRealLayoutDirection(): Int =
if (ViewCompat.isLayoutDirectionResolved(this)) {
layoutDirection
} else {
resolveLayoutDirection()
}
Run Code Online (Sandbox Code Playgroud)
现在打电话View.getRealLayoutDirection()并获得你想要的价值.
请注意,此方法在很大程度上依赖于访问AOSP中存在的隐藏API,但可能不会出现在供应商实现中.彻底测试!
| 归档时间: |
|
| 查看次数: |
1430 次 |
| 最近记录: |