Background
It's possible to get the current locale direction, using this:
val isRtl=TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
It's also possible to get the layout direction of a view, if the developer has set it:
val layoutDirection = ViewCompat.getLayoutDirection(someView)
The problem
The default layoutDirection of a view isn't based on its locale. It's actually LAYOUT_DIRECTION_LTR .
When you change the locale of the device from LTR (Left-To-Right) locale (like English) to RTL (Right-To-Left) locale (like Arabic or Hebrew) , the views will get aligned accordingly, yet the values you get by default of the views will stay LTR...
This means that given a view, I don't see how it's possible to determine the correct direction it will go by.
What I've tried
I've made a simple POC. It has a LinearLayout with a TextView:
<?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>
In code, I write the direction of the locale, and of the views:
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"
}
}
The result is that even when I switch to RTL locale (Hebrew - עברית), it prints this in logs:
locale direction:isRTL? true
linearLayout direction:LAYOUT_DIRECTION_LTR
textView direction:LAYOUT_DIRECTION_LTR
And of course, the textView is aligned to the correct side, according to the current locale:
If it would have worked as I would imagine (meaning LAYOUT_DIRECTION_LOCALE by deafult), this code would have checked if a view is in RTL or not:
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
}
But it can't, because LTR is the default one, and yet it doesn't even matter...
So this code is wrong.
The questions
How could it be that by default, the direction is LTR, yet in practice it gets aligned to the right, in case the locale has changed?
How can I check if a given View's direction would be LTR or RTL , no matter what the developer has set (or not set) for it ?
getResources().getConfiguration().locale
in place ofLocale.getDefault()
? Does that return RTL correctly? – CoperTextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
) . I've updated the question to make it clearer – Bathysphereandroid:supportsRtl="true"
on your application tag in manifest? If you look at the source ofView.resolveLayoutDirection
it only works in such case. – CoperView.onMeasure
. When you move logging toActivity.onResume
, does it print correct values? – CoperonResume
. However, if I useHandler().postDelayed(...,1000)
, it works. Is there any way to check it out as soon as possible? Even on onCreate ? And, why is it LTR by default? This value is supposed to be set if it should be by force, no? – BathysphereView.resolveRtlPropertiesIfNeeded
through reflection beforeView.getLayoutDirection
should do the trick. – CoperView.isAttachedToWindow
returns true.View.post
(instead ofpostDelayed
) is good enough. I'll start writing an answer. – Coper