Android 14 新特性:语法性别 Grammatical Gender

在这里插入图片描述

背景

如同汉语里的他、她、它,英语里的 He、She、it,很多语言都存在依据性别、对象不同而造成的语法差异,甚至不仅限于名词,还涉及到形容词、动词等,复杂得多。

而这部分语言所涉及到的人群多达 30 亿之众,如果文本只使用通用的、中性的表述,则显得不够准确。假使不加区分,甚至对女性使用男性化的表述方式,则体验更为糟糕。

所以如果 App 的界面语言能正确反映用户的语法性别,则就可以提高用户好感度、互动度,达到更个性化、更自然的用户体验。

这便是 Android 14 推出的重要新特性:语法性别 Grammatical Gender

语法性别管理 API

首先,Android 14 推出了针对语法性别的 API:GrammaticalInflectionManager,其提供了针对单个 App 获取、设置性别偏好的入口。

  • getApplicationGrammaticalGender():获取语法性别偏好,返回的是 Configuration 类中的 int 常量,有这么几种类型:

    • GRAMMATICAL_GENDER_NOT_SPECIFIED, 0:尚未指定性别偏好,将用默认的资源文本
    • GRAMMATICAL_GENDER_NEUTRAL, 1:指定中性、客观的资源文本
    • GRAMMATICAL_GENDER_FEMININE, 2:指定针对女性的资源文本
    • GRAMMATICAL_GENDER_MASCULINE, 3:指定针对男性的资源文本
  • setRequestedApplicationGrammaticalGender():相对应的将上述常量类型动态设置到性别偏好

设置性别资源

反映语法性别的变化还得有依据语法性别配置的文本才行。而法语针对语法性别的差异比较典型,我们选择法语文本进行示例说明。

比如我们添加 res/values-fr-feminine 目录,在其 strings.xml 里添加针对女性的表述方式。

 <resources>
     ...
     <string name="example_string">Vous êtes abonnée à...</string>
 </resources>

并添加 res/values-fr-masculine 目录,添加针对男性的表述方式。

 <resources>
     ...
     <string name="example_string">Vous êtes abonné à...</string>
 </resources>

在 res/values-fr 下的 strings.xml 里添加针对中性、客观的表述方式。

 <resources>
     ...
     <string name="example_string">Abonnement à...activé</string>
 </resources>

另外在 values/strings.xml 添加默认的英文表述。

 <resources>
     <string name="example_string">You are subscribed to our store service</string>
 </resources>

动态设置性别偏好

然后我们添加个 TextView 来展示语法性别变化的效果,首先看一下默认 Gender 即 NOT_SPECIFIED 时的文本表述。

Screenshot_20230616_174557.png

可以看到使用的是 values 下默认的英文表述。

然后,在 Button 点击里模拟各种性别偏好的动态更新。

 class GenderActivity : AppCompatActivity() {
     private lateinit var gIM: GrammaticalInflectionManager
     private lateinit var binding: GenderLayoutBinding
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         Log.d("Gender", "onCreate()")
 
         binding = GenderLayoutBinding.inflate(layoutInflater)
         setContentView(binding.root)
 
         gIM = getSystemService(GrammaticalInflectionManager::class.java)
         updateCurrentGender()
 
         binding.changeGender.setOnClickListener {
             Log.d("Gender", "change Gender button tapped")
 
             gIM.setRequestedApplicationGrammaticalGender(
                 gIM.applicationGrammaticalGender.nextGender()
             )
 
             updateCurrentGender()
         }
     }
 
     private fun updateCurrentGender() {
         gIM.applicationGrammaticalGender.genderToString().let {
             Log.d("Gender", "current gender: $it")
             binding.changeGender.text = "Grammatical gender:$it"
         }
     }
 
     private fun Int.nextGender() =
         when (this) {
             Configuration.GRAMMATICAL_GENDER_NEUTRAL -> Configuration.GRAMMATICAL_GENDER_MASCULINE
             Configuration.GRAMMATICAL_GENDER_MASCULINE -> Configuration.GRAMMATICAL_GENDER_FEMININE
             Configuration.GRAMMATICAL_GENDER_FEMININE -> Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED
             Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED -> Configuration.GRAMMATICAL_GENDER_NEUTRAL
             else -> Configuration.GRAMMATICAL_GENDER_NEUTRAL
         }
 }

运行下看下变化效果,可以看到 Gender 在 NOT_SPECIFIED、NEUTRAL、MASCULINE 和 FEMININE 配置之间切换,TextView 的表述也相应地进行了更新。

update-gender-recreate.gif

Log 也展示了当前 Gender 和变化的记录:

 ellisonchan@bogon AndroidUDemo % adb logcat -s Gender
 06-16 17:47:33.160  D Gender  : onCreate()
 06-16 17:47:33.228  D Gender  : current gender: Not specified
 06-16 17:47:49.103  D Gender  : change Gender button tapped
 06-16 17:47:49.238  D Gender  : onCreate()
 06-16 17:47:49.261  D Gender  : current gender: Neutral
 06-16 17:47:49.325  D Gender  : current gender: Neutral
 06-16 17:47:55.771  D Gender  : change Gender button tapped
 06-16 17:47:55.845  D Gender  : onCreate()
 06-16 17:47:55.863  D Gender  : current gender: Masculine
 06-16 17:47:55.904  D Gender  : current gender: Masculine
 06-16 17:47:59.634  D Gender  : change Gender button tapped
 06-16 17:47:59.666  D Gender  : onCreate()
 06-16 17:47:59.683  D Gender  : current gender: Feminine
 06-16 17:47:59.742  D Gender  : current gender: Feminine
 06-16 17:48:01.735  D Gender  : change Gender button tapped
 06-16 17:48:01.773  D Gender  : onCreate()
 06-16 17:48:01.788  D Gender  : current gender: Not specified
 06-16 17:48:01.846  D Gender  : current gender: Not specified

需要留意的是,上述效果需要将设备或 App 偏好语言设置成法语才可以看到效果。

还有个细节要注意,调用完 setRequestedApplicationGrammaticalGender 更新 Gender 后,通过 getApplicationGrammaticalGender() 的处理要稍微延迟一下,才能看到新的偏好,也可以理解,因为这个设置是 GrammaticalInflectionManager 系统服务通知的 App Context,这个过程是异步的。

 class GenderActivity : AppCompatActivity() {
     ...
     override fun onCreate(savedInstanceState: Bundle?) {
         ...
         binding.changeGender.setOnClickListener {
             ...
 
             gIM.setRequestedApplicationGrammaticalGender(
                 gIM.applicationGrammaticalGender.nextGender()
             )
 
             // 延迟一下再去获取最新 Gender 偏好
             GlobalScope.launch(Dispatchers.Main) {
                 delay(50)
                 updateCurrentGender()
             }
         }
     }
     ...
 }

语法性别变更的重绘防止

上面的录屏、log 都可以看到 Gender 偏好变化之后 Activity 会发生重绘,因为它本质上也属于 Configuration 的范畴。和其他 Configuration change 一样,如有需要防止画面重启,可以在 Manifest 中配置。

 <?xml version="1.0" encoding="utf-8"?>
 <manifest ...>
     ...
     <application...>
         <activity
             ...
             android:configChanges="grammaticalGender">
         </activity>
     </application>
 </manifest>

可想而知,Activity 不自动刷新,改了 Gender TextView 也不会有效果了。

update-gender-not-recreate.gif

但好在该 Gender 偏好随着 onConfigurationChanged() 被传递了过来。

 06-16 18:05:31.021  D Gender  : onCreate()
 06-16 18:05:31.021  D Gender  : current gender: Not specified
 
 06-16 18:06:33.023  D Gender  : change Gender button tapped
 06-16 18:06:33.229  D Gender  : GenderActivity# onConfigurationChanged() new gender:Neutral
 
 06-16 18:06:34.541  D Gender  : change Gender button tapped
 06-16 18:06:34.578  D Gender  : GenderActivity# onConfigurationChanged() new gender:Masculine
 ...

我们可以选择使用 ConfigurationgetGrammaticalGender() 来获取新的 Gender 偏好,也可以使用上述的 GrammaticalInflectionManager 获取 API,两者得到的性别偏好结果是一致的。

 class GenderActivity : AppCompatActivity() {
     ...
     override fun onConfigurationChanged(newConfig: Configuration) {
         super.onConfigurationChanged(newConfig)
         Log.d("Gender", "GenderActivity# onConfigurationChanged()" +
                 " new gender:${newConfig.grammaticalGender.genderToString()}"
         )
     }
 }

既然可以拿到新的 Gender,那么让 TextView 被动刷新即可,而刷新的关键使用新的 Resources 获取当前 Gender Configuration 下的 text 去更新。

 class GenderActivity : AppCompatActivity() {
     ...
     override fun onConfigurationChanged(newConfig: Configuration) {
         ...
 
         // Resources will be updated with new configuration
         val newText = resources.getString(R.string.example_string)
         Log.d("Gender", "onConfigurationChanged()" +
                 " new text:${resources.getString(R.string.example_string)}"
         )
 
         binding.textview.text = newText
     }
 }

可以看到这次只是 TextView 局部刷新,不会有全体的重绘、闪烁。

update-gender-not-recreate-update.gif

需要留意,如下两个方法是无效成功更新 Gender 效果的:

  • invalidate(),因为它只是触发重新测量、布局和描画,text 内容并无变化
  • dispatchConfigurationChanged(),因为 TextView 的 onConfigurationChanged() 只针对 Locale、Typeface 进行了配置刷新,没有针对 Gender 做更新

注意

即便在最新的 Release 版 AS 上,都是无法识别上述语法性别资源目录的,会发生编译失败:

AndroidUDemo/app/src/main/res/values-fr-feminine: Error: Invalid resource directory name

得升级到 Android Studio Giraffe Canary 7 或更高的版本,才能支持。

另外还得升级到最新的 AGP,笔者使用的版本组合是:Android Studio Hedgehog | 2023.1.1 Canary 7 + AGP 8.1.0-alpha07,供大家参考。

DEMO 源码

https://github.com/ellisonchan/AndroidUDemo

参考

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
英语的常用词根词缀有: 1. 词根(Root):常常是一个单词的核心部分,可以用来构建其他单词。例如,act(行动)是一个词根,可以衍生出active(积极的)、action(行动)、actor(演员)等单词。 2. 前缀(Prefix):通常出现在单词的开头,可以改变词的含义或词性。例如,un-(不)是一个常见的前缀,可以将happy(快乐)变成unhappy(不快乐)。 3. 后缀(Suffix):通常出现在单词的结尾,可以改变词的词性或形态。例如,-ly(副词后缀)可以将adjective(形容词)变成adverb(副词),例如quick(快速的)变成quickly(快速地)。 4. 合成词(Compound words):由两个或更多的词根、前缀或后缀组合而成的单词。例如,sunflower(向日葵)由sun和flower组成。 5. 派生词(Derivatives):由一个词根加上前缀或后缀构成的单词。例如,enjoyment(享受)由enjoy和-ment(名词后缀)组成。 6. 表示数量的词缀(Numerical prefixes):例如bi-(两个)和tri-(三个)等可以用来表示数量的前缀。 7. 表示时间的词缀(Temporal prefixes):例如pre-(之前)和post-(之后)等可以用来表示时间的前缀。 8. 表示空间的词缀(Spatial prefixes):例如inter-(之间)和intra-(内部)等可以用来表示空间的前缀。 9. 表示语法的词缀(Grammatical suffixes):例如-ly(副词后缀)和-able(形容词后缀)等可以用来表示语法的后缀。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechMerger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值