👉 square/radiography: Text-ray goggles for your Android UI.
dependencies {
implementation 'androidx.compose.ui:ui-tooling:1.0.0-betaXY'
implementation 'com.squareup.radiography:radiography:2.4.1'
}
scan() すると、画面のツリー構造がテキストで取得できるようでです。
DecorView { 1080×2160px }
├─LinearLayout { id:main, 1080×1962px }
│ ├─EditText { id:username, 580×124px, focused, text-length:0, ime-target }
│ ├─EditText { id:password, 580×124px, text-length:0 }
│ ╰─LinearLayout { 635×154px }
│ ├─Button { id:signin, 205×132px, text-length:7 }
│ ╰─Button { id:forgot_password, 430×132px, text-length:15 }
├─View { id:navigationBarBackground, 1080×132px }
╰─View { id:statusBarBackground, 1080×66px }
// Render the view hierarchy for all windows.
val prettyHierarchy = Radiography.scan()
// Extension function on View, renders starting from that view.
val prettyHierarchy = someView.scan()
Jetpack Compose でやってみる
この画面でやってみます。
Composable にコードを追加しておきます。
@Composable
fun HomeScreen(
viewModel: HomeViewModel = hiltViewModel()
) {
// ...
val view: View = LocalView.current
SideEffect {
println(Radiography.scan())
println(view.scan())
}
}
👉 SideEffect - Compose における副作用 | Jetpack Compose | Android Developers
結果
I: window-focus:false
I: DecorView { 1080×2160px }
I: ├─LinearLayout { 1080×2116px }
I: │ ├─ViewStub { id:action_mode_bar_stub, GONE, 0×0px }
I: │ ╰─FrameLayout { id:content, 1080×2039px }
I: │ ╰─ComposeView { 1080×2039px }
I: │ ╰─AndroidComposeView { 1080×2039px }
I: │ ╰─CompositionLocalProvider { 1080×2039px }
I: │ ╰─ScaffoldLayout { 1080×2039px }
I: │ ├─<subcomposition of ScaffoldLayout>
I: │ ├─<subcomposition of ScaffoldLayout>
I: │ │ ╰─SnackbarHost
I: │ ├─<subcomposition of ScaffoldLayout>
I: │ ├─<subcomposition of ScaffoldLayout>
I: │ │ ╰─CompositionLocalProvider { 1080×154px }
I: │ │ ├─NavBottomBar { 1080×154px }
I: │ │ │ ╰─Row { 1080×154px, SELECTABLE-GROUP }
I: │ │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ │ ╰─BottomNavigationTransition { 162×154px }
I: │ │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ │ ├─Icon
I: │ │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ │ ╰─Box { 96×44px, layout-id:"label" }
I: │ │ │ │ ╰─ProvideTextStyle { 96×44px, text-length:4 }
I: │ │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ │ ╰─BottomNavigationTransition { 151×154px }
I: │ │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ │ ├─Icon
I: │ │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ │ ╰─Box { 85×44px, layout-id:"label" }
I: │ │ │ │ ╰─ProvideTextStyle { 85×44px, text-length:5 }
I: │ │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ │ ╰─BottomNavigationTransition { 156×154px }
I: │ │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ │ ├─Icon
I: │ │ │ │ │ │ ├─RenderVectorGroup
I: │ │ │ │ │ │ ├─RenderVectorGroup
I: │ │ │ │ │ │ ├─RenderVectorGroup
I: │ │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ │ ╰─Box { 90×44px, layout-id:"label" }
I: │ │ │ │ ╰─ProvideTextStyle { 90×44px, text-length:5 }
I: │ │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ │ ╰─BottomNavigationTransition { 150×154px }
I: │ │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ │ ├─Icon
I: │ │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ │ ╰─Box { 84×44px, layout-id:"label" }
I: │ │ │ │ ╰─ProvideTextStyle { 84×44px, text-length:4 }
I: │ │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ │ ╰─BottomNavigationTransition { 146×154px }
I: │ │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ │ ├─Icon
I: │ │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ │ ╰─Box { 80×44px, layout-id:"label" }
I: │ │ │ │ ╰─ProvideTextStyle { 80×44px, text-length:4 }
I: │ │ │ ╰─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ ╰─BottomNavigationTransition { 180×154px }
I: │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ ├─Icon
I: │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ ╰─Box { 114×44px, layout-id:"label" }
I: │ │ │ ╰─ProvideTextStyle { 114×44px, text-length:8 }
I: │ │ ╰─AdaptiveAd { 1080×0px }
I: │ │ ╰─ViewFactoryHolder { 1080×0px }
I: │ │ ╰─AdView { 1080×0px }
I: │ │ ╰─FrameLayout { 0×0px }
I: │ ╰─<subcomposition of ScaffoldLayout>
I: │ ╰─NavHost
I: │ ╰─Box
I: │ ╰─LocalOwnersProvider
I: │ ├─TopBar
I: │ │ ╰─CompositionLocalProvider
I: │ │ ├─Row
I: │ │ │ ╰─CompositionLocalProvider { roll:Button }
I: │ │ │ ├─CompositionLocalProvider
I: │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ ╰─CompositionLocalProvider
I: │ │ ├─Row
I: │ │ │ ╰─ProvideTextStyle { text-length:4 }
I: │ │ ╰─CompositionLocalProvider
I: │ ╰─Column
I: │ ├─Button { roll:Button }
I: │ │ ╰─CompositionLocalProvider
I: │ │ ╰─Text { text-length:7 }
I: │ ├─Spacer
I: │ ├─Button { roll:Button }
I: │ │ ╰─CompositionLocalProvider
I: │ │ ╰─Text { text-length:7 }
I: │ ├─Spacer
I: │ ├─Button { roll:Button }
I: │ │ ╰─CompositionLocalProvider
I: │ │ ╰─Text { text-length:7 }
I: │ ├─Spacer
I: │ ╰─Button { roll:Button }
I: │ ╰─CompositionLocalProvider
I: │ ╰─Text { text-length:5 }
I: ├─View { id:navigationBarBackground, 1080×44px }
I: ╰─View { id:statusBarBackground, 1080×77px }
I: AndroidComposeView:
I: window-focus:false
I: AndroidComposeView { 1080×2039px }
I: ╰─CompositionLocalProvider { 1080×2039px }
I: ╰─ScaffoldLayout { 1080×2039px }
I: ├─<subcomposition of ScaffoldLayout>
I: ├─<subcomposition of ScaffoldLayout>
I: │ ╰─SnackbarHost
I: ├─<subcomposition of ScaffoldLayout>
I: ├─<subcomposition of ScaffoldLayout>
I: │ ╰─CompositionLocalProvider { 1080×154px }
I: │ ├─NavBottomBar { 1080×154px }
I: │ │ ╰─Row { 1080×154px, SELECTABLE-GROUP }
I: │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ ╰─BottomNavigationTransition { 162×154px }
I: │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ ├─Icon
I: │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ ╰─Box { 96×44px, layout-id:"label" }
I: │ │ │ ╰─ProvideTextStyle { 96×44px, text-length:4 }
I: │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ ╰─BottomNavigationTransition { 151×154px }
I: │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ ├─Icon
I: │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ ╰─Box { 85×44px, layout-id:"label" }
I: │ │ │ ╰─ProvideTextStyle { 85×44px, text-length:5 }
I: │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ ╰─BottomNavigationTransition { 156×154px }
I: │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ ├─Icon
I: │ │ │ │ │ ├─RenderVectorGroup
I: │ │ │ │ │ ├─RenderVectorGroup
I: │ │ │ │ │ ├─RenderVectorGroup
I: │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ ╰─Box { 90×44px, layout-id:"label" }
I: │ │ │ ╰─ProvideTextStyle { 90×44px, text-length:5 }
I: │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ ╰─BottomNavigationTransition { 150×154px }
I: │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ ├─Icon
I: │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ ╰─Box { 84×44px, layout-id:"label" }
I: │ │ │ ╰─ProvideTextStyle { 84×44px, text-length:4 }
I: │ │ ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ │ ╰─BottomNavigationTransition { 146×154px }
I: │ │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ │ ├─Icon
I: │ │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ │ ╰─Icon { 66×66px }
I: │ │ │ ╰─Box { 80×44px, layout-id:"label" }
I: │ │ │ ╰─ProvideTextStyle { 80×44px, text-length:4 }
I: │ │ ╰─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I: │ │ ╰─BottomNavigationTransition { 180×154px }
I: │ │ ├─Box { 66×66px, layout-id:"icon" }
I: │ │ │ ├─Icon
I: │ │ │ │ ╰─RenderVectorGroup
I: │ │ │ ╰─Icon { 66×66px }
I: │ │ ╰─Box { 114×44px, layout-id:"label" }
I: │ │ ╰─ProvideTextStyle { 114×44px, text-length:8 }
I: │ ╰─AdaptiveAd { 1080×0px }
I: │ ╰─ViewFactoryHolder { 1080×0px }
I: │ ╰─AdView { 1080×0px }
I: │ ╰─FrameLayout { 0×0px }
I: ╰─<subcomposition of ScaffoldLayout>
I: ╰─NavHost
I: ╰─Box
I: ╰─LocalOwnersProvider
I: ├─TopBar
I: │ ╰─CompositionLocalProvider
I: │ ├─Row
I: │ │ ╰─CompositionLocalProvider { roll:Button }
I: │ │ ├─CompositionLocalProvider
I: │ │ │ ╰─RenderVectorGroup
I: │ │ ╰─CompositionLocalProvider
I: │ ├─Row
I: │ │ ╰─ProvideTextStyle { text-length:4 }
I: │ ╰─CompositionLocalProvider
I: ╰─Column
I: ├─Button { roll:Button }
I: │ ╰─CompositionLocalProvider
I: │ ╰─Text { text-length:7 }
I: ├─Spacer
I: ├─Button { roll:Button }
I: │ ╰─CompositionLocalProvider
I: │ ╰─Text { text-length:7 }
I: ├─Spacer
I: ├─Button { roll:Button }
I: │ ╰─CompositionLocalProvider
I: │ ╰─Text { text-length:7 }
I: ├─Spacer
I: ╰─Button { roll:Button }
I: ╰─CompositionLocalProvider
I: ╰─Text { text-length:5 }
どう使います?、これ。
scan() 時のオプションもいろいろあるようなので試してみますか。