React ã®ã¨ã³ã¸ãã¢ã Jetpack Compose ãåå¼·ãã¦ã¿ã
è²ä¼å㯠React 㨠go ãããæ¸ãã¦ãã¾ããããè²ä¼ãçµãã£ã¦ Android ã®ä»äºã«ã¤ããã¨ã«ãªã£ãã®ã§ãJetpack Compose ã®åå¼·ããã¾ãããã¨ãã£ã¦ãèªåã°ãºãã®ç´ 人ã¨ããããã§ã¯ãªããJetpack Compose ãå°å ¥ãããåã«ã¯ Android ã®ã¨ã³ã¸ãã¢ã§ã確ããã«ãã¢ã¸ã¥ã¼ã«ãä¸ã®ä¸ã«åºå§ããããªããããã®æ代㧠Web æ¹é¢ã«é²ãã ã®ã§ããããã¸ãã®æ代ã§ç¥èãæ¢ã¾ã£ã¦ãã¾ãã
Jetpack Compose ã¨ã¯
https://developer.android.com/jetpack/compose
Android ãã¤ãã£ãã¢ããªã±ã¼ã·ã§ã³ãã宣è¨ç UI ã§ã¢ããªãä½ããã¨ãã§ãããããã§ããããã¾ã§ Android 㯠Activityï¼ããã°ã¦ã£ã³ãã¦ãä¸ç»é¢ã«å¿
ãï¼ã¤ãããªãï¼ ã Fragmentï¼ããã°ã¦ã£ã³ãã¦ã®ä¸ã«ããããView ã®åå°ã¿ãããªãã®ã1ç»é¢ãã¤ã¾ã Activity ä¸ã«è¤æ°ç½®ããã¨ãã§ããï¼ ã¨ããä¸ã«ãxml ã§ã¬ã¤ã¢ã¦ããæå®ï¼HTMLã¿ãããªæãï¼ãã¦ãæç»ããã View ã«å¯¾ããActivity ã Fragment ã«ã³ã¼ããæ¸ã㦠View ããã¤ã³ãããããã¯ãªãã¯ã¤ãã³ããè¨è¿°ããããã¦ãã¾ããã
Jetpack Compose ã§ã¯ãåå°ã¯ Activity ã Fragment ã§ã¯ãããã®ã®ããã®2ã¤ã¯ãã£ã±ã Jetpack Compose ã®ã«ã¼ãè¦ç´ ç½®ãå ´ã¨ãã¦æ©è½ãã¾ããxml ã§ã¬ã¤ã¢ã¦ããæ¸ãææ³ã¯å»æ¢ããã表示ç¨ã³ã¼ãã¨ãã¸ãã¯ãåä½ãã¦ãããããªæè¦ã§ãããã®ãããéã«ãã㨠React ã¿ãããªãã¤ã§ãã
React ã®ç¥èã¨ç §ããåããã¦åå¼·
https://developer.android.com/courses/android-basics-compose/course
https://developer.android.com/courses/pathways/compose
ããã¥ã¡ã³ããã³ã¼ãã©ãããã£ã¦ããã¾ãããã³ã¼ãã©ãã§ã³ã¼ããæ¸ããªãããããã㯠React ã§ãããããªãã ãªãã¨ããçªãåãããããªããç解ãã¦ããã¾ããããã®ä¸ã§ Compose ãªãã§ã¯ã®ç解ãæ·±ãã¦ããã¨è¯ãã®ããªã¨æã£ã¦ãã¾ãã
React ã§è¨ãã³ã³ãã¼ãã³ã㯠Composable ãªé¢æ°ã§è¡¨ã
React ã§ä»¥ä¸ã®ããã«æ¸ãã¨ããã¡ã³ãã«å¤æ
ã¨æ¸ãããããã¹ãã表示ããã¾ãã
function Title() { return <div>ãã¡ã³ãã«å¤æ </div>; }
ããã Android ã§ãããã¨ããã¨ãããªãããã§ãã
@Composable fun Title() { Text(text = "ãã¡ã³ãã«å¤æ ") }
ãªãã¨ãªããWeb ã¨ã³ã¸ãã¢ã§ã親ãã¿ãããæãã«ãªã£ã¦ããã®ã§ã¯ãªãã§ããããããã¯ã宣è¨ç UI ã¨ããã ããã£ã¦ React ã«ããä¼¼ã¦ãã¾ããReact ã¨éã£ã¦ Class ã§ã³ã³ãã¼ãã³ãã¯ä½ãããå ¨ã¦é¢æ°ã§ UI ãä½ãã¾ããã¾ãã³ã³ãã¼ãã³ãã¨ã¯è¨ããã³ã³ãã¼ã¶ãã«(composable)ã¨è¨ãã¾ããUIã¯ãç¶æ ãå¤åãããã³ã³ãã¼ãº(compose)ããããã®éã«ã³ã³ãã¼ã¶ãã«ãªé¢æ°ãã²ã¨ãããå¼ã°ãåæç»ãããä»çµã¿ã§ãã
ããä¸ç¹ã大ããªéããããã¾ããããã¯æç»ããè¦ç´ ãæ»ãå¤ã¨ãã¦è¿ãå¿ è¦ããããããªããã¨ããé¨åã§ãã
ä»ä¸åº¦ React ã®ã³ã¼ããè¦ã¦ã¿ãã¨
function Title() { return <div>ãã¡ã³ãã«å¤æ </div>; }
ReactElement
ãè¿ãã¦ããã®ããããã¾ããã¤ã¾ããUI ã¯é¢æ°ã®æå¾ã§ã¾ã¨ã㦠return
ããå¿
è¦ãããã¾ãã
ã¨ããã Jetpack Compose ã®é¢æ°ã¯è¿ãå¤ã¯ Unit
ã§ãããã㯠Typescript ã§ãã Void
ã«ç¸å½ãã¾ããããªãã¡ãUI ãé¢æ°ã®æå¾ã§è¿ãå¿
è¦ã¯ãªãã
@Composable fun Title() { Text(text = "ãã¡ã³ãã«å¤æ ") val mes = "xxxxx" Box { Text(text = "詳細") println(mes) } println(mes) }
ãããã£ãè¨è¿°ãå¯è½ãªããã§ãã
Compose ã§æé«ã«æ¥½ã«ãªã£ãã®ã¯ãªã¹ãã§ã¯ãªãã§ãããããå¾æ¥ã¯ RecyclerView
ã¨ãã View ã¯ã©ã¹ãå®è£
ãããã¨ã§ãªã¹ããä½ã£ã¦ãã¾ãããããã¯Adapter ã¨ã ViewHolder ã¨ãç»å ´äººç©ãå°ãå¤ããç´°ããªã³ã¼ããæ¸ãã¦ãªã¹ãä¸ã® item ãã©ã®ããã«ä½¿ãåãããæ¸ãå¿
è¦ãããã¾ãããããããªãã¨ã¹ã¯ãã¼ã«æã«ããã©ã¼ãã³ã¹ãæªåããããã§ãã
Compose ã ã¨ãã®ããã«æ¸ãã¾ã
LazyColumn {
items(items = items, key = { item -> item.id }) {
WorkingTimesListItem(
item = it,
onClickDetail = onClickDetail
)
}
}
key ãã¤ãã¦ããã®ã¯ React çµé¨è
ãªããã³ã¨ãããã®ã§ããã¨æãã¾ããã¡ãªã¿ã« Kotlin ã¯é¢æ°ã®å¼æ°ã§ãä¸çªãããã«ããã©ã ãã¯ãé¢æ°ã®å¼æ°ãããã¯ãé£ã³åºãã¦ã{ ... }
ãããã¯ã使ã£ã¦æ¸ããã¨ãåºæ¥ã¾ãã LazyColumn
ããæ¬æ¥ã§ããã° LazyColumn(content: ããã« Composable ãªé¢æ°ãæ¸ã)
ã¨ãªãã®ã§ãããè¨èªæ©è½ã«ãã£ã¦ããæ¸ããã¨ãã§ããããã宣è¨ç UI ã¨ãã¦ãã¹ããã¦ãã£ãã¨ãã¦ãèªã¿ãããã親åæ§ã®é«ããã®ã«ãªã£ã¦ããæ°ããã¾ãã
ç¶æ
å
¨ã¦ã¯åã³ã³ãã¼ãºãããã®ã§ããã®ã¾ã¾ã ã¨ã³ã³ãã¼ãºé¢æ°å
ã«ç¶æ
ãä¿æãç¶ãããã¨ã¯åºæ¥ã¾ãããReact ã§ã¯ useState
ã使ã£ã¦ããããã¾ãããCompose ã«ãã«ããã®ã¯ç¨æããã¦ãã¾ãã
function XXX() { ... var isClicked by remember { mutableStateOf(false) } ...
ãã宣è¨ããã°ãisClicked
ã¯åã³ã³ãã¼ãºå¾ã§ã失ãããªãã®ã§ãç¶æ
ãä¿æã§ãã¾ããby
㯠Property ãå§è²ãã Kotlin ã®è¨èªæ©è½ã§ããKotlin ã§ã¯ãä»åã®ããã«å®£è¨ãã var ã®å ´å㯠ãããã©ã«ã㧠setter 㨠getter ãçããã®ã§ããããã® setter 㨠getter ãèªåã§æå®ã§ãã¾ãã
var name: String = name set(value) { field = value.toLowerCase().capitalize() updateCount++ }
ãã㧠by ã使ãã¨ãsetter 㨠getter ã«ä½¿ãé¢æ°ï¼ã¤ã¾ããsetter 㨠getter ãå®è£
ããé¢æ°ï¼ãæå®ã§ãã¾ããããã«ãã£ã¦ãsetterãgetter ã®ãã¸ãã¯ãæ½åºãããã¹ãããããããããåå©ç¨æ§ãåä¸ãããã®ã§ãããCompose ã¯ãã®æ©è½ãå©ç¨ãã¦ãç°¡åã«ç¶æ
ãä¿æããäºã®ã§ãã remember
ãæä¾ãã¦ãããã¨ãããã¨ã«ãªãã¾ããããã¦ãã® remember
ãã³ã³ãã¼ã¶ãã«ã§ãã
ãã¸ãã¹ãã¸ãã¯ãå ¼ãåãããç¶æ ãèããå ´åãViewModel ãå©ç¨ãããã¨ã«ãªãã¾ããhttps://developer.android.com/jetpack/compose/state#viewmodels-source-of-truth
詳細ã¯ãã¡ã https://developer.android.com/jetpack/compose/state
å¯ä½ç¨
è¤éãªã¢ããªãä½ã£ã¦ããã¨ã©ããã¦ãå¯ä½ç¨ã使ããããå¾ãªãã±ã¼ã¹ãåºã¦ãã¾ããCompose ã¯å¯ä½ç¨ããµãã¼ããã¦ãã¾ãã
useEffect 㨠LaunchedEffect
ä¾ãã°React ãæãè¿ãã¨ãuseEffect
ãããã¨æãã¾ãã
useEffect(() => { update(); });
ããã«å¯¾å¿ãããã®ã¨ãã¦ãLaunchedEffect
ãããã¾ãã
LaunchedEffect(Unit) {
update()
}
ã©ã¡ããåçæã«ä¼´ãã«å¯ä½ç¨ãèµ°ããã®ã«ãªã£ã¦ãã¾ããã¤ã¾ããã©ã¡ããã³ã³ãã¼ãã³ããComposable ã®ã©ã¤ããµã¤ã¯ã«ã«æ²¿ã£ãå½¢ã§å¯ä½ç¨ãè¡ã£ã¦ããã¨ãããã¨ã«ãªãã¾ãã両è ã¨ããæ°ããå¯ä½ç¨ãå®è¡ããã¨ãã«ãååã®å¯ä½ç¨ã¯ãã£ã³ã»ã«ããã¾ãã
ããã ç¹å®ã®å¤ãå¤æ´ãããå ´åã®ã¿å¯ä½ç¨ãèµ°ããããã¨ãã
ã¨ãReact ã§ã¯ä»¥ä¸ã®ããã«ãªãã¾ããä¾ãã° id ãå¤ãã£ãå ´åã«ã®ã¿ update()
ã試ã¿ãå ´åã
const [id, setId] = useState(defaultId); useEffect(() => { update(id) }, [id]);
Composable ã§ã¯ã©ãã§ããããï¼
@Composable var id by remember { mutableStateOf(defaultId) } LaunchedEffect(id) { update(id) }
LaunchedEffect
é¢æ°ã«ããã1ã¤ç®ã®å¼æ°ã¯ React ã® dependencies array
ã¨ç¸ä¼¼ãã¦ãã¾ãã両è
ã¨ã id ãå¤åããªããã°ãåçæããã¦ãåã³å®è¡ããã¾ããã
LaunchedEffect
ã«ãããé¢æ°ã¯ã³ã«ã¼ãã³ã¹ã³ã¼ãå
ã§å®è¡ããã¾ããã¤ã¾ã API ãå©ãããã ã¨ã㧠UI ããããã¯ããããã¨ã¯ããã¾ããããsuspend
é¢æ°ã¨ããéåæå¦çãå¼ã¶ãã¨ãåºæ¥ã¾ãã
ã¨ããã§ãReact ã§ã¯ useEffect
ã«ã¯ã¯ãªã¼ã³ã¢ããé¢æ°ãã¤ãããã¨ãåºæ¥ã¾ãã
useEffect(() => { subscribe() return function cleanup() { unsubscribe() } });
ãã㯠Compose ã«ããã¦ã¯ DisposableEffect
ã使ã£ã¦è¡¨ç¾ã§ãã¾ããã¯ãªã¼ã³ã¢ããé¢æ°ãã¤ããã¨è¨ãããããã¯ãªã¼ã³ã¢ããé¢æ°ãå¿
è¦ãªå ´å㯠DisposableEffect
ã使ããã¨ããæãã§ãã
DisposableEffect(Unit) {
subscribe()
onDispose {
unsubscribe()
}
}
React ã§ãã Context
React ã§ã¯ãå¤å±¤ãªé層ã§ç¶æ ãä¼éããã¦ããã¨ãã±ããªã¬ã¼ã大å¤ã§ãããã¨ãããcontext ã®å©ç¨ãèãããã¨ãããã¾ããhttps://ja.reactjs.org/docs/context.html
ããã¾ã§é »ç¹ã«å©ç¨ãããã®ã§ããªãæ°ããã¾ãããä¾ãã° react-hook-form ãªãã使ãã¨ä½é¨ãããã¨ãããã®ã§ã¯ãªãã§ããããã
ä¼¼ããã®ã¯ Compose ã«ãç¨æããã¦ãã¾ãããCompositionLocal
ã§ãã
https://developer.android.com/jetpack/compose/compositionlocal
ä¾ã§ã¯ããã¶ã¤ã³ã«ä½¿ãè²ããããã®é層ã§æ±ºããã¨ãã¦ãããã®è²ãé¥ãä¸ã®é層ã§ä½¿ãå ´åã¯ãã£ã¡ããã±ããªã¬ã¼ãå¿
è¦ã§ä¸ä¾¿ã§ãããã±ããªã¬ã¼ãããããã¾ãããã¨ãããã®ã§ãã
ã¾ã èªåã§ã¯ä½¿ã£ããã¨ãããã¾ãããã èªåã¯ã¢ããªã®ãã¶ã¤ã³ããããªã¢ã«ãã¶ã¤ã³ã§çµ±ä¸ãã MaterialTheme
object ã¯è§¦ã£ã¦ãããè²ããæåã®ãµã¤ãºãããæ§ã
ãªé層㮠Composable ã§è§¦ããã¨ã«ãªãã®ã§ãããããã«ã CompositionLocal
ã®å®è£
ã使ããã¦ããã¿ããã§ãã
ããã²ã¼ã·ã§ã³
èªåãéå»ã«é¢ãã£ããããã¯ãã§ã¯ãããã³ãã¨ã³ãã®ããã²ã¼ã·ã§ã³ã«ã¯ React ã Next.js ä¸ã§åããããã㧠Router ã使ã£ã¦ãã¾ããNext.js ã§ã¯ãã¨ããã Web ã«ã¯ URL ãããã¾ãã®ã§ãåã
ã®å ´æã§URL ãæå®ãã¦é·ç§»ããã¨æãã¾ãã Composable ã§ã¯ããã²ã¼ã·ã§ã³ãå¸ã Composalbe NavHost
ãç¨æããã¦ããã®ã§ãé常ã¯ãã¡ããå©ç¨ãã¾ãã
ã¾ããé·ç§»ãã Composable ãå
¨é¨ NavHost
Composable ã§å²ã¿ã¾ããããã§é·ç§»ããç»é¢ãç»é²ããã¤ã¡ã¼ã¸ã§ãã
https://developer.android.com/jetpack/compose/navigation#create-navhost
NavHost(navController = navController, startDestination = "profile") { composable(route = "profile") { Profile(navController = navController) } composable(route = "friendslist") { FriendsList(navController = navController) } /*...*/ }
NavHost ã親ã¨ããProfile
Composable ã FriendList
Composable ãé·ç§»å(route
)ã¨ç´ä»ãã¦ç½®ãã¦ããã¾ããé·ç§»ããã¨ãã¯ããã®é·ç§»åã使ããNavController
ã使ã£ã¦é·ç§»ãã¾ããnavController 㯠NavHost
ããåãåãã¾ãã
@Composable fun Profile(navController: NavController) { /*...*/ Button(onClick = { navController.navigate("friendslist") }) { Text(text = "Navigate next") } /*...*/ }
route
ã§æå®ãã¦ããæååã¯ãå®æ°åããããã¦è¨è¿°ãã¹ãé²ãã®ãä¸è¬çãªããã§ãã
ãã¶ã¤ã³
React ã§ã¯ãå DOM ã«å¯¾ã㦠style
props ãæå®ããã®ãæãåå§çãªæ¹æ³ã«ãªãã¾ãã
<p style={backgroundColor:"red"}>Color</p>
åãã¢ããã¼ã㯠Composable ã«ãç¨æããã¦ãã¾ããModifier
ã§ãã
Text(
text = "Color",
modifier = Modifier.background(color = Color.Red)
)
ãã ããReact ã§ç´æ¥ style props ã渡ãã®ã¯ãããã©ã¼ãã³ã¹ã®é¢ã§ãã管çã®é¢ã§ãããããããããã®ã§ãã¹ã¿ã¤ã«ãå¥ã«å®ç¾©ã className ã§ã¹ã¿ã¤ã«åãæå®ããã®ãä¸è¬çã§ãã
<p className="title">Color</p>
Composable ã®å ´åããã¶ã¤ã³ãèªç±èªå¨ãª Web ã¨ã¯å°ãäºæ ãç°ãªãã¾ããã¨ããã®ããAndroid ã¨ãããã©ãããã©ã¼ã ã®ä¸ã§åã以ä¸ãAndroid ã®ä¸ç観ãåæ ãããã¶ã¤ã³ã«ããå¿ è¦æ§ãããããã§ãããã®å ´åããã¼ãæ©è½ã使ã£ã¦ãã¶ã¤ã³ãçµ±ä¸ããã®ãä¸è¬çãªããã§ãã
https://developer.android.com/jetpack/compose/themes/material
ãã㯠MaterialTheme
Composalbe ã使ããã¨ã«ãªãã¨æãã¾ããå
¨ä½ã«ãã¼ããå½ã¦ããã®ã§ãæããããã®é層㧠MaterialTheme
Composable ãç½®ãã¦ããã®ä¸ã«æ§ã
㪠Composable ãç½®ãã¦ããæãã§ãã以ä¸ã®ä¾ã§ã¯ãisSystemInDarkTheme()
ã使ã£ã¦ããã¼ã¯ã¢ã¼ãä¸ã§ããã°æãè²ãæå®ããããã«ãã¦ãã¾ãã
val Teal200 = Color(0xFF03DAC5) val Indigo200 = Color(0xFF9FA8DA) val Indigo500 = Color(0xFF3F51B5) val Indigo700 = Color(0xFF303F9F) val BlueGray50 = Color(0xFFECEFF1) val BlueGray900 = Color(0xFF263238) private val DarkColorPalette = darkColors( primary = Indigo200, primaryVariant = Indigo700, secondary = Teal200, background = BlueGray900 ) private val LightColorPalette = lightColors( primary = Indigo500, primaryVariant = Indigo700, secondary = Teal200, background = BlueGray50 ) @Composable fun TryJetpackComposeTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val colors = if (darkTheme) { DarkColorPalette } else { LightColorPalette } MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) } @Composable fun TryJetpackComposeApp() { TryJetpackComposeTheme { val navController = rememberNavController() val items = listOf(Screen.Home.name, Screen.Stamp.name) Scaffold(...) { ....
å¾æ¥ã§ã¯ãè²ã¯ colors.xml
ã«è¨è¿°ãã¦ãã£ã¦ããããåå°ã§ä½¿ãã®ãä¸è¬çã§ããããä»åããã¯å¤æ°ã§è²ãå®ç¾©ã®ãæ¨å¥¨ããã¦ãã¾ãã
ææ
ãã£ã±ã宣è¨ç UI ã¨ãããã¨ã§ React ã®ç¥èã¨çµã³ã¤ãããã¨ãã§ãããã®ã«ãªã£ã¦ãã¾ããAndroid åºæã®ã©ã¤ããµã¤ã¯ã«ãæèããå ´é¢ã¯å§åçã«å°ãªããªããReact ã¨åããããªã©ã¤ããµã¤ã¯ã«ãæèããã°è¯ããªã£ãã®ã¯å¬ããã§ãã
ã³ã¼ããæ¸ãéãåæ¸ããã¦ãã¦ãæã®ããã« Activity ã Fragment ãç¨æãã¦ãXML ã«ã¬ã¤ã¢ã¦ãããããDataBinding 㧠bind ãã¦ããªã¹ãã®å ´å㯠RecyclerView ã®è«¸ã ã®ã³ã¼ããç¨æãã¦...ã¨ããæéãä¸æã«ç¡ããªã£ãã®ãå¬ããã§ããã«ã¹ã¿ã View çãªãã®ãä½ãããããªãã¾ãããéçºã«æéããããã®ã Android ã®ããã¯ãªé¨åã ãªãã¨æã£ã¦ããã®ã§ããããã®å¼±ç¹ããªããªã£ãå°è±¡ã§ãã
ã¾ã Google å ¬å¼ã§ããã¥ã¡ã³ããã³ã¼ãã©ããå å®ãã¦ããããã確ããªç¥èãç´ æ©ãã¤ãããã¨ãã§ããç¹ãé åã§ãã