この記事は、フラー株式会社のカレンダー | Advent Calendar 2023 - Qiitaの9日目の記事です1。
8日目はいのりこ (id:inoriko711) さんで弊社エンジニアリンググループ初、産休取得してみたでした。
はじめに
デザインの観点から、複数のLightやMediumといった色々なウエイトのフォントを使いたいということがあります。しかし、Androidの標準では日本語フォントはRegularとBoldの2種類しか表示できないです2。
画像のように他のウエイトを設定しても、日本語フォントではRegularかBoldのどちらかで表示されることになります。
コード
val textStyle = TextStyle(
platformStyle = PlatformTextStyle(includeFontPadding = false),
lineHeight = 1.2.em,
)
@Preview
@Composable
fun MulchWeights1() {
Column(modifier = Modifier.padding(8.dp)) {
for (weight in 100..900 step 100) {
Text(
text = "サンプル「『』」 weight=$weight",
style = textStyle,
fontWeight = FontWeight(weight),
)
}
}
}
このような場合は、そのウエイトに対応したフォントをGoogle Fontsなどからダウンロードして対応します。
[完]
これだけでは面白くないので、こだわりを持って複数ウエイトに対応する場合を考えます。
Androidに搭載される日本語フォントについて
Android 12以降に搭載されているフォントにはchws (Contextual Half-width Spacing) により約物が連続した時のアキを調整する機能があります3。
Google FontsのNoto Sans JPではchwsの機能のないttfしかダウンロードできません。安直にこのフォントを設定した場合複数ウエイトに対応することはできても、約物が残念になってしまいます。
コード
@OptIn(ExperimentalTextApi::class)
val notoSansJpGoogleFonts = FontFamily(
(100..900 step 100).map { weight ->
Font(
resId = R.font.noto_sans_jp_google_fonts,
weight = FontWeight(weight),
variationSettings = FontVariation.Settings(FontWeight(weight), FontStyle.Normal)
)
}
)
val textStyle = TextStyle(
platformStyle = PlatformTextStyle(includeFontPadding = false),
lineHeight = 1.2.em,
)
@Preview
@Composable
fun MulchWeights2() {
Column(modifier = Modifier.padding(8.dp)) {
for (weight in 100..900 step 100) {
Text(
text = "サンプル「『』」 weight=$weight",
style = textStyle,
fontWeight = FontWeight(weight),
fontFamily = notoSansJpGoogleFonts,
)
}
}
}
chws機能付きのNoto Sans JPを求めて
chws機能がついたNoto Sans JPを探したところsimonsmh/notocjkというリポジトリを見つけました。このリポジトリではAndroidデバイスに含まれるパッチを加えた全てのウエイトのNoto Sans CJK / Noto Serif CJKを公開しています。
日本語のSansだけが必要なため、NotoSansCJK-VF.otf.ttc をダウンロードし、日本語の部分のotfを取得します4。
これにより複数ウエイトかつ約物の間隔が綺麗な表示に対応できます。
コード
@OptIn(ExperimentalTextApi::class)
val notoSansJpWithPatch = FontFamily(
(100..900 step 100).map { weight ->
Font(
resId = R.font.noto_sans_jp_with_patch,
weight = FontWeight(weight),
variationSettings = FontVariation.Settings(FontWeight(weight), FontStyle.Normal)
)
}
)
val textStyle = TextStyle(
platformStyle = PlatformTextStyle(includeFontPadding = false),
lineHeight = 1.2.em,
)
@Preview
@Composable
fun MulchWeights3() {
Column(modifier = Modifier.padding(8.dp)) {
for (weight in 100..900 step 100) {
Text(
text = "サンプル「『』」 weight=$weight",
style = textStyle,
fontWeight = FontWeight(weight),
fontFamily = notoSansJpWithPatch,
)
}
}
}
バリアブルフォントを使用しているので、100より細かい単位でウエイトを変えることもできます。
コード
@OptIn(ExperimentalTextApi::class)
val notoSansJpWithPatch = FontFamily(
(400..500 step 10).map { weight ->
Font(
resId = R.font.noto_sans_jp_with_patch,
weight = FontWeight(weight),
variationSettings = FontVariation.Settings(FontWeight(weight), FontStyle.Normal)
)
}
)
val textStyle = TextStyle(
platformStyle = PlatformTextStyle(includeFontPadding = false),
lineHeight = 1.2.em,
)
@Preview
@Composable
fun MulchWeights4() {
Column(modifier = Modifier.padding(8.dp)) {
for (weight in 400..500 step 10) {
Text(
text = "サンプル「『』」 weight=$weight",
style = textStyle,
fontWeight = FontWeight(weight),
fontFamily = notoSansJpWithPatch,
)
}
}
}
最後に
今回の方法では、標準のRoboto + Noto Sans JPにならず、すべてNoto Sans JPになります。その話はまたいつの日か…



