用Jetpack Compose写一个玩安卓App!
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 即可。
为了防止失联,欢迎关注我的小号
                
                   微信改了推送机制,真爱请星标本公号👇
                 
              评论
