用Jetpack Compose写一个玩安卓App!
刘望舒
共 27783字,需浏览 56分钟
·
2021-07-30 19:23
BAT
作者:Zhujiang
https://juejin.cn/user/3913917127985240/posts
前言
Compose 和 Flutter
Compose 为什么会出现
Compose 有什么
准备工作
准备工作
引入依赖
// `Compose`
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.activity:activity-compose:1.3.0-alpha03"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha02"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$compose_version"
implementation "androidx.compose.foundation:foundation:$compose_version"
implementation "androidx.compose.foundation:foundation-layout:$compose_version"
implementation "androidx.compose.material:material-icons-extended:$compose_version"
androidTestImplementation "androidx.compose.ui:ui-test:$compose_version"
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
// navigation
implementation "androidx.navigation:navigation-compose:1.0.0-alpha08"
android {
…………
buildFeatures {
`Compose` true
viewBinding true
}
`Compose`Options {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion kotlin_version
}
}
新建 Activity
<activity
android:name=".`Compose`.NewMainActivity"
android:theme="@style/AppTheme.NoActionBars">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
class NewMainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PlayTheme {
Home()
}
}
}
}
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
) {
val existingComposeView = window.decorView
.findViewById<ViewGroup>(android.R.id.content)
.getChildAt(0) as? ComposeView
if (existingComposeView != null) with(existingComposeView) {
setParentCompositionContext(parent)
setContent(content)
} else ComposeView(this).apply {
// Set content and parent **before** setContentView
// to have ComposeView create the composition on attach
setParentCompositionContext(parent)
setContent(content)
setContentView(this, DefaultActivityContentLayoutParams)
}
}
创建 Compose
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/hello_world"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Android!" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
return inflater.inflate(
R.layout.fragment_example, container, false
).apply {
findViewById<ComposeView>(R.id.compose_view).setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
}
}
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
MaterialTheme {
// In Compose world
Text("Hello Compose!")
}
}
}
}
}
class ExampleFragment : Fragment() {
override fun onCreateView(...): View = LinearLayout(...).apply {
addView(ComposeView(...).apply {
id = R.id.compose_view_x
...
})
addView(TextView(...))
addView(ComposeView(...).apply {
id = R.id.compose_view_y
...
})
}
}
}
<resources>
<item name="compose_view_x" type="id" />
<item name="compose_view_y" type="id" />
</resources>
PlayTheme
@Composable
fun PlayTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) {
PlayThemeDark
} else {
PlayThemeLight
}
MaterialTheme(
colors = colors,
typography = typography,
content = content
)
}
private val PlayThemeLight = lightColors(
primary = blue,
onPrimary = Color.White,
primaryVariant = blue,
secondary = blue
)
private val PlayThemeDark = darkColors(
primary = blueDark,
onPrimary = Color.White,
secondary = blueDark,
surface = blueDark
)
val blue = Color(0xFF2772F3)
val blueDark = Color(0xFF0B182E)
val Purple300 = Color(0xFFCD52FC)
val Purple700 = Color(0xFF8100EF)
画页面
@Composable
fun Home() {
}
底部导航栏
@Composable
fun Home() {
ComposeDemoTheme {
val (selectedTab, setSelectedTab) = remember { mutableStateOf(CourseTabs.HOME_PAGE) }
val tabs = CourseTabs.values()
Scaffold(
backgroundColor = MaterialTheme.colors.primarySurface,
bottomBar = {
BottomNavigation(
Modifier.navigationBarsHeight(additional = 56.dp)
) {
tabs.forEach { tab ->
BottomNavigationItem(
icon = { Icon(painterResource(tab.icon), contentDescription = null) },
label = { Text(stringResource(tab.title).toUpperCase()) },
selected = tab == selectedTab,
onClick = { setSelectedTab(tab) },
alwaysShowLabel = false,
selectedContentColor = MaterialTheme.colors.secondary,
unselectedContentColor = LocalContentColor.current,
modifier = Modifier.navigationBarsPadding()
)
}
}
}
) { innerPadding ->
val modifier = Modifier.padding(innerPadding)
when (selectedTab) {
CourseTabs.HOME_PAGE -> One(modifier)
CourseTabs.PROJECT -> Two(modifier)
CourseTabs.OFFICIAL_ACCOUNT -> Three(modifier)
CourseTabs.MINE -> Four(modifier)
}
}
}
}
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
scaffoldState: ScaffoldState = rememberScaffoldState(),
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
isFloatingActionButtonDocked: Boolean = false,
drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
drawerGesturesEnabled: Boolean = true,
drawerShape: Shape = MaterialTheme.shapes.large,
drawerElevation: Dp = DrawerDefaults.Elevation,
drawerBackgroundColor: Color = MaterialTheme.colors.surface,
drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
drawerScrimColor: Color = DrawerDefaults.scrimColor,
backgroundColor: Color = MaterialTheme.colors.background,
contentColor: Color = contentColorFor(backgroundColor),
content: @Composable (PaddingValues) -> Unit
)
enum class CourseTabs(
@StringRes val title: Int,
@DrawableRes val icon: Int
) {
HOME_PAGE(R.string.home_page, R.drawable.ic_nav_news_normal),
PROJECT(R.string.project, R.drawable.ic_nav_tweet_normal),
OFFICIAL_ACCOUNT(R.string.official_account, R.drawable.ic_nav_discover_normal),
MINE(R.string.mine, R.drawable.ic_nav_my_normal)
}
管理状态
https://developer.android.google.cn/reference/kotlin/androidx/compose/runtime/package-summary#mutableStateOf(androidx.compose.runtime.mutableStateOf.T
添加页面
@Composable
fun One(modifier: Modifier) {
Text(modifier = modifier
.fillMaxSize()
.padding(top = 100.dp), text = "One", color = Teal200)
}
@Composable
fun Two(modifier: Modifier) {
Text(modifier = modifier
.fillMaxSize()
.padding(top = 100.dp), text = "Two", color = Teal200)
}
@Composable
fun Three(modifier: Modifier) {
Text(modifier = modifier
.fillMaxSize()
.padding(top = 100.dp), text = "Three", color = Teal200)
}
@Composable
fun Four(modifier: Modifier) {
Text(modifier = modifier
.fillMaxSize()
.padding(top = 100.dp), text = "Four", color = Teal200)
}
总结
https://github.com/zhujiang521/PlayAndroid
推荐阅读
• 耗时2年,Android进阶三部曲第三部《Android进阶指北》出版!
BATcoder技术群,让一部分人先进大厂
大家好,我是刘望舒,腾讯TVP,著有三本技术畅销书,连续四年蝉联电子工业出版社年度优秀作者,谷歌开发者社区特邀讲师,百度百科收录的p8+级专家。
前华为技术专家,现大厂技术负责人。
想要加入 BATcoder技术群,公号回复BAT
即可。
为了防止失联,欢迎关注我的小号
微信改了推送机制,真爱请星标本公号👇
评论