Jetpack Compose is easy to enter the pit. It's all about advanced salary increase

Keywords: Android Design Pattern

Here, we can learn:

  • 1. Compose is a declarative UI, not a traditional command UI. Instead of holding View to setXXX(), it uses functions to describe a UI state.

  • 2. Compose adopts the idea of attribute refinement and behavior isolation. For example, in the demo, text is the direct attribute of text, and background is an attribute of modifier. Because Image has no text attribute, but has background attribute, compose extracts the public attribute into the modifier and binds the unique attribute to the View itself, This is an idea of attribute refinement, and the click event is in the modifier, that is, the behavior is controlled through the modifier, which is the separation of behavior and attribute. In other words, the View itself is only responsible for describing its own inherent attributes, and the modifier is responsible for describing extended attributes and behaviors. In this way, the design is more humanized and reduces the coupling in disguise.

practice

Let's look at a function. This is Google's Android Study Jam class ending quiz, which requires the implementation of a Searchbar.

First look at the effect:

Very simple. Enter keywords and click search to list the items containing keywords. If the keywords are empty, all items will be listed. If we use the native implementation, we may need to write a RecyclerView, an Adapter, a ViewHolder... Let's look at the composition version:

1. Implement upper Searchbar
@Composable

fun SearchBar(onSearch: (keyword: String) -> Unit) {

    var value by remember { mutableStateOf("") }

    OutlinedTextField(

        label = { Text(text = "Please input keyword") },

        value = value,

        onValueChange = { value = it },

        modifier = Modifier

            .padding(horizontal = 16.dp)

            .fillMaxWidth(),

        singleLine = true,

        textStyle = TextStyle(color = Color.DarkGray, fontWeight = FontWeight.W700),

        trailingIcon = @Composable { Image(imageVector = Icons.Filled.Clear, contentDescription = null, Modifier.clickable { value = "" }) },

        keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),

        keyboardActions = KeyboardActions(onSearch = { onSearch.invoke(value) }),

    )

}

Copy code 

The function receives an onSearch() method and passes a String; OutlinedTextField can be understood as EditText, and value is equivalent to the text attribute of EditText. trailingIcon represents the tail icon, that is, the closed X. when clicking it, we set value to an empty String, which is equivalent to clearing. keyboardOptions represents the icon of keyboard return, which we define as ImeAction.Search, that is, search; keyboardActions refers to the method executed by clicking return. We directly call onSearch(), as shown in the figure:

The red x is specified by trailingIcon, and the search is specified by keyboardOptions.

2. Implement the following list
@Composable

fun ContentList(contentData: ContentsListData) {

    val contents by contentData.getDisplayData().observeAsState(listOf())

    LazyColumn(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.Start) {

        items(contents) { content ->

            Text(text = content, modifier = Modifier.padding(4.dp))

        }

    }

}

Copy code 

Function receives a LiveData; Among them, LazyColumn can be understood as RecyclerView in Compose, and items is an internal lambda, which means expanding the list of contents and constructing Text. The size of contents will construct as many Text as possible.

3. Combined UI
@Composable

fun HomeScreen() {

    Column {

        SearchBar { keyword ->

            Toast.makeText(this@MainActivity, "keyword is $keyword", Toast.LENGTH_SHORT).show()

            viewModel.filter(keyword = keyword)

        }



        Spacer(modifier = Modifier.size(8.dp))



        ContentList(contentData = viewModel)

    }

}

Copy code 

Column is equivalent to LinearLayout, and Spacer is equivalent to a blank View, which is used as a dividing line.

4 data part

The ContentsListData code is as follows:

interface ContentsListData {

    fun getDisplayData(): MutableLiveData<MutableList<String>>

}



class MainViewModel : ViewModel(), ContentsListData {

    // All data

    var totalDatas = MutableLiveData<MutableList<String>>()



    // Data to be displayed

    val displayDatas = MutableLiveData<MutableList<String>>()



    init {

        // Add test data

        val totalList = mutableListOf<String>()

        for (index in 1..100) {

            totalList.add("content $index")

        }

        val displayList = mutableListOf<String>()

        totalList.forEach { displayList.add(it) }



        totalDatas.value = totalList

        displayDatas.value = displayList



    }



    fun filter(keyword: String) {



        val list: MutableList<String> = mutableListOf()

        if (keyword.isEmpty()) {

            // If the keyword is empty, all data will be displayed

            totalDatas.value?.forEach { list.add(it) }

        } else {
 

Posted by phelpsa on Wed, 24 Nov 2021 02:19:04 -0800