Как предотвратить принудительное размещение LTR в RTL на некоторых устройствах Android?
После реализации поддержки RTL в моем приложении пользователь сообщил, что текст LTR принудительно переводится как RTL в арабских локалях. Это выглядит следующим образом:
Обратите внимание, как текст выровнен по правому краю, даже если он должен быть выровнен по левому краю.
Пользователь сказал мне, что эта ошибка присутствует только на
- Huawei Y5 Prime 2018 версия 8.1.0
- и LG G3 версия 6.0.0
он отсутствует в Samsung Galaxy J7 версии 6.0.1 или в эмуляторе Android с SDK 23, 26, 27, 28. Там он правильно отображает LTR в локалях RTL, как показано на этом снимке экрана:
Я не могу воспроизвести его в эмуляторе - там текст LTR отображается с выравниванием по левому краю, а текст RTL (в данном случае арабский) правильно отображается с выравниванием по правому краю. Я подозреваю, что основной причиной является то, что некоторые производители добавили код для принудительного размещения макетов RTL в локалях RTL.
Обратите внимание, что поскольку мое приложение предназначено для чтения RSS-каналов, у меня нет контроля над отображаемым текстом, поэтому я вынужден полагаться на Bidi-алгоритмы (которые работают нормально! За исключением этих устройств).
Я уточняю android:supportsRtl="true"
в манифесте:
<application
android:allowBackup="true"
android:name=".FeederApplication"
android:icon="@mipmap/ic_launcher_round"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/AppTheme">
...
</application>
И это макет со скриншота:
<com.nononsenseapps.feeder.views.ObservableScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.nononsenseapps.feeder.ui.ReaderFragment">
<!-- Action bar is overlayed, so add some padding -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="@dimen/keyline_1"
android:paddingTop="?actionBarSize"
android:paddingEnd="@dimen/keyline_1"
android:paddingBottom="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/story_title"
style="@style/TextAppearance.Reader.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:textDirection="anyRtl"
android:textIsSelectable="true"
android:transitionName="title"
tools:text="@tools:sample/cities" />
<TextView
android:id="@+id/story_feedtitle"
style="@style/TextAppearance.Reader.Author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:textDirection="locale"
android:textIsSelectable="true"
tools:text="CowboyProgrammer" />
<TextView
android:id="@+id/story_author"
style="@style/TextAppearance.Reader.Author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_marginBottom="8dp"
android:textDirection="locale"
android:textIsSelectable="true"
tools:text="Jonas, Sep 14 2015" />
<com.nononsenseapps.feeder.views.LinkedTextView
android:id="@+id/story_body"
style="@style/TextAppearance.Reader.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:minHeight="300dp"
android:textDirection="anyRtl"
android:textIsSelectable="true"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>
</com.nononsenseapps.feeder.views.ObservableScrollView>
В случае, если вам интересно, где находится ImageView - их нет. Изображение, которое вы видите на скриншоте, включено как ImageSpan
внутри LinkedTextView
,
я использую anyRtl
чтобы получить правильное форматирование смешанного текста. Поведение по умолчанию (firstStrong
) будет отображать текст как выровненный по левому краю, если он начинается с английского слова в противном случае. Это код, устанавливающий текст в TextViews:
val viewModel = getFeedItemViewModel(_id)
viewModel.liveItem.observe(this, androidx.lifecycle.Observer {
rssItem = it
rssItem?.let { rssItem ->
setViewTitle()
// feedDisplayTitle is a SpannableString
mFeedTitleTextView.text = rssItem.feedDisplayTitle
rssItem.pubDate.let { pubDate ->
rssItem.author.let { author ->
when {
author == null && pubDate != null ->
mAuthorTextView.text = getString(R.string.on_date,
pubDate.withZone(DateTimeZone.getDefault())
.toString(dateTimeFormat))
author != null && pubDate != null ->
mAuthorTextView.text = getString(R.string.by_author_on_date,
// Must wrap author in unicode marks to ensure it formats
// correctly in RTL
unicodeWrap(author),
pubDate.withZone(DateTimeZone.getDefault())
.toString(dateTimeFormat))
else -> mAuthorTextView.visibility = View.GONE
}
}
}
}
})
viewModel.liveImageText.observe(this, androidx.lifecycle.Observer {
// the liveImageText is a SpannableString
bodyTextView.text = it
})
// [...]
fun Fragment.unicodeWrap(text: String): String =
BidiFormatter.getInstance(getLocale()).unicodeWrap(text)
fun Fragment.getLocale(): Locale? =
context?.getLocale()
fun Context.unicodeWrap(text: String): String =
BidiFormatter.getInstance(getLocale()).unicodeWrap(text)
fun Context.getLocale(): Locale =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
resources.configuration.locales[0]
} else {
@Suppress("DEPRECATION")
resources.configuration.locale
}
Как я уже сказал, я не контролирую отображаемый текст, а просто конвертирую (возможно, в формате HTML) текст в SpannableString
перед отображением ( используя этот код).
Итак, как говорится в заголовке, кто-нибудь знает способ добавить обходной путь для конкретных устройств, где возникает эта ошибка?
1 ответ
Возможно ли, что у вашего пользователя есть приложение на языке RTL, и устройство просто пропускает ответ алгоритма Bidi при построении текста в некоторых случаях?