A Preliminary Study of Compose

A Preliminary Study of Compose

Preparation before use

  1. android studio

    Arctic Fox
    Version or newer

  2. If it is a new project, you can create a new one when creating

    Empty Compose Activity

  3. In the module

    build.gradle
    Add in file

    android { buildFeatures { compose true } composeOptions { kotlinCompilerExtensionVersion compose_version kotlinCompilerVersion '1.4.32' } } dependencies { implementation'androidx.core :core-ktx:1.3.2' implementation'androidx.appcompat :appcompat:1.2.0' implementation'com.google.android.material :material:1.3.0' implementation "androidx.compose.ui: ui:$compose_version" implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.ui:ui-tooling:$compose_version" implementation'androidx.lifecycle :lifecycle-runtime-ktx:2.3.1' implementation 'androidx.activity:activity-compose:1.3.0-alpha06' } Copy code

Need to add

buildFeatures { compose true } Copy code

Component

Definition of components

A UI component in Compose is a

@Composable
Annotated function

@Composable fun Greeting (name: String ) { Text(text = "Hello $name !" ) } Copy code

Layout component

If the layout component is not used, and the single view is written directly to a Compose, there will be an abnormal situation. The official said:

A Composable function might emit several UI elements. However, if you don't provide guidance on how they should be arranged, Compose might arrange the elements in a way you don't like

  • Row
    The view is arranged horizontally, and the related attributes of Row are as follows:
    inline fun Row ( modifier: Modifier = Modifier, horizontalArrangement: Arrangement . Horizontal = Arrangement.Start, verticalAlignment: Alignment . Vertical = Alignment.Top, Content: @ Composables that RowScope . () -> Unit ) Copy the code
  • Column
    Vertically arrange the view, its properties and the above
    Row
    similar
  • Box
    Overlay one element on top of another, similar to
    FrameLayout
    This kind of

View component

  • Text
    Similar to TextView in native View
  • Button
    Button
  • LazyColumn
    Similar to native RecyclerView
  • Image
    Picture control Regarding network pictures, you can use
    Coil
    frame
  • TextField
    File input box
  • Surface
    Used to control the background, border, text color, etc. of the component
  • AlertDialog
    Pop-up window controls, similar to those in native View
    AlertDialog

Component state management

remember

by

remember
To record the value of some related properties of the component, when the property changes, it will automatically trigger the update of the UI.

@Composable fun HelloContent () { Column(modifier = Modifier.padding( 16. dp)) { var nameState = remember {mutableStateOf( "" )} var name = nameState.value; Text( text = "Hello, $name !" , modifier = Modifier.padding(bottom = 8. dp), style = MaterialTheme.typography.h5 ) TextField( value = name, onValueChange = {println( "data-----> $it " );nameState.value = it} ) } } Copy code

The function implemented by this code is that when the user enters text in an input box, it will be instantly echoed on the page. When encoding in this way, the state is coupled in the component. When the caller does not care about the internal state, this method is ok, but its drawback is that it is not conducive to the reuse of the component. We can separate the state from the component. At this time, we use the state promotion (

state hoisting
)s method

@Composable fun HelloScreen () { var nameState = remember {mutableStateOf( "" )} HelloContent(name = nameState.value, onNameChange = {nameState.value = it }) } @Composable fun HelloContent (name: String , onNameChange: ( String ) -> Unit ) { Column(modifier = Modifier.padding( 16. dp)) { Text( text = "Hello, $name !" , modifier = Modifier.padding(bottom = 8. dp), style = MaterialTheme.typography.h5 ) TextField( value = name, onValueChange = {onNameChange(it)} ) } } Copy code

Here is the state mentioned

HelloContent
Outside
HelloContent
Component reuse

rememberSaveable

with

remember
Similar, the difference is
rememberSaveable
When performing state management, when the activity or process is re-created (such as screen rotation), its state information will not be lost. Add the above
var nameState = remember {mutableStateOf("")}
middle
remember
Replace with
rememberSaveable
That's it

ViewModel

usable

ViewModel
Carry out global state management

class HelloViewModel : ViewModel () { //LiveData holds state which is observed by the UI //(state flows down from ViewModel) private val _name = MutableLiveData( "" ) val name: LiveData<String> = _name //onNameChange is an event we're defining that the UI can invoke //(events flow up from UI) fun onNameChange (newName: String ) { _name.value = newName } } @Composable fun HelloScreen (helloViewModel: HelloViewModel = viewModel() ) { //by default, viewModel() follows the Lifecycle as the Activity or Fragment //that calls HelloScreen(). This lifecycle can be modified by callers of HelloScreen. //name is the current value of [helloViewModel.name] //with an initial value of "" val name: String by helloViewModel.name.observeAsState( "" ) HelloContent(name = name, onNameChange = {helloViewModel.onNameChange(it) }) } Copy code

Modifers

Modifers are used to decorate composable, and Modifiers are used to tell a UI element how to layout, display, and related behaviors.

Layout-related attributes

  • fillMaxWidth
  • matchParentSize
  • height
  • width
  • padding
  • size

display

  • background
  • clip
    : Such as
    Modifier.clip(RoundedCornerShape(4.dp))
    , A fillet came out

Bound event

use

clickable
To bind events

Row( Modifier .fillMaxWidth() .clickable {onClick(); }, verticalAlignment = Alignment.CenterVertically ) { ... } Copy code

Instance

The development experience of using the Compose solution is very close to using Vue or React. The code structure is very clear. It does save a lot of things without using xml to draw the UI. The following is a code snippet to draw a WeChat personal center page

@Preview(showBackground = true) @Composable fun PersonalCenter () { Column() { Header( "Hello World" , "Wechat_0001" ) Divider( Modifier .fillMaxWidth() .height( 8. dp), GrayBg ) RowList() Divider( Modifier .fillMaxHeight(), GrayBg ) } } @Composable fun Header (nickName: String , wechatNo: String ) { Row( Modifier .fillMaxWidth() .padding( 24. dp, 24. dp, 16. dp, 24. dp), verticalAlignment = Alignment.CenterVertically ) { Image( painter = painterResource(R.drawable.avatar), contentDescription = "Avatar" , Modifier .size( 50. dp) .clip( RoundedCornerShape( 4. dp) ) ) Column() { Text(nickName, Modifier.padding( 12. dp, 2. dp, 0. dp, 0. dp), TextColor, fontSize = 18. sp) Row(verticalAlignment = Alignment.CenterVertically) { Text( " Wechat : $wechatNo " , Modifier .padding( 12. dp, 10. dp, 0. dp, 0. dp) .weight( 1.0f ), TextColorGray, fontSize = 14. sp ) Icon(painterResource(R.drawable.ic_qrcode), "QR code" , Modifier.size( 16. dp)) Icon( painterResource(R.drawable.right_arrow_3), contentDescription = "more" , Modifier.padding( 12. dp, 0. dp, 0. dp, 0. dp) ) } } } } @Composable fun RowItem ( @DrawableRes icon: Int , title: String , onClick: () -> Unit ) { Row( Modifier .fillMaxWidth() .clickable {onClick(); }, verticalAlignment = Alignment.CenterVertically ) { Image( painter = painterResource(icon), contentDescription = title + "icon" , Modifier .padding( 16. dp, 12. dp, 16. dp, 12. dp) .size( 24. dp) ) Text(title, Modifier.weight( 1f ), TextColor, fontSize = 15. sp) Icon( painterResource(R.drawable.right_arrow_3), contentDescription = "more" , Modifier.padding( 0. dp, 0. dp, 16. dp, 0. dp) ) } } @Composable fun RowList () { var context = LocalContext.current; Column() { RowItem(icon = R.drawable.ic_pay, title = "payment" ) {onItemClick(context, "payment" )} Divider( Modifier .fillMaxWidth() .height( 8. dp), GrayBg ) RowItem(icon = R.drawable.ic_collections, title = "Collection" ) { onItemClick(context, "Collection" ) } Divider( Modifier .fillMaxWidth() .padding( 56. dp, 0. dp, 0. dp, 0. dp) .height( 0.2 .dp), GrayBg ) RowItem(icon = R.drawable.ic_photos, title = "Album" ) { onItemClick(context, "album" ) } Divider( Modifier .fillMaxWidth() .padding( 56. dp, 0. dp, 0. dp, 0. dp) .height( 0.2 .dp), GrayBg ) RowItem(icon = R.drawable.ic_cards, title = "card package" ) { Toast.makeText(context, "payment" , Toast.LENGTH_SHORT).show() } Divider( Modifier .fillMaxWidth() .padding( 56. dp, 0. dp, 0. dp, 0. dp) .height( 0.2 .dp), GrayBg ) RowItem(icon = R.drawable.ic_stickers, title = " Emotion " ) { Toast.makeText(context, "payment" , Toast.LENGTH_SHORT).show() } Divider( Modifier .fillMaxWidth() .height( 8. dp), GrayBg ) RowItem(icon = R.drawable.ic_settings, title = "Settings" ) { Toast.makeText(context, "payment" , Toast.LENGTH_SHORT).show() } } } fun onItemClick (context: Context , data : String ) { Toast.makeText(context, data , Toast.LENGTH_SHORT).show() } Copy code

Compose embedded in View

var view = LinearLayout(this) view.addView(ComposeView(this).apply { setContent { PersonalCenter(); } }) Copy code

Embedded View in Compose

@Compose fun RowList () { ... AndroidView({View(context)}, Modifier.width( 20. dp).height( 20. dp).background(Color.Green)){} ... } Copy code

summary

  • Compose uses a new set of layout and rendering mechanism. The elements in it are different from the various Views we wrote before. For example, the Text in Compose is not what we knew before.
    TextView
    Or other native controls, it uses a lower-level API to achieve
  • Automatic subscription of data (complete two-way binding)
  • Declarative UI: compose completes the automatic update of the UI through an automatic subscription mechanism
  • Mix compose and existing native View

reference