Search code examples
androidkotlinandroid-activity

ComponentActivity with Kotlin


My generated MainActivity in Kotlin confuses me a lot:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    Greeting("Android")
                }
            }
        }
    }

    fun Greeting(name: String, modifier: Modifier = Modifier) {
        Text(
            text = "Hello $name!",
            modifier = modifier
        )
    }

    fun GreetingPreview() {
        MyAppTheme {
            Greeting("Android")
        }
    }
}
  • setContent is declared public fun ComponentActivity.setContent(parent: CompositionContext? = null, content: @Composable () -> Unit) in a file ComponentActivity.kt but there seems to be no relationship to the class ComponentActivity. Can you extend a class in Kotlin in an extra file if you just write the classname + dot in front of new methods?

  • MyAppTheme is a function in a file Theme.kt but with no relationship to a class and no return type. What does it mean to write it followed by by a block with curly brackets?

  • Then we have a method call of Surface also followed by a curly brackets block

  • Greeting creates a Text instance that is not returned so it must be lost and garbage collected.

I must have missed all that stuff in the W3schools kotlin tutorial. I haven't found a ComponentActivity with kotlin tutorial googeling. Maybe someone can enlight me about those cryptic constructs? This is quite different from java and I dont know if for the better.


Solution

  • Most of what you're asking has to do with Jetpack Compose, which is a DSL (domain specific language) built on top of Kotlin. It's not typical Kotlin code.

    First bullet point: This does apply to any Kotlin code. You can write extension functions with that syntax. You are not extending the class. You are writing a function that can be called as if it were a member of that class, but behavior-wise it's really just an external, static function that uses the class type as an argument.

    The point of this Kotlin feature is it makes utility functions very easy to write and use--code looks much more natural. For example:

    Arrays.sort(myIntArray); // java utility method call
    

    vs.

    myIntArray.sort() // kotlin extension function call
    

    Second bullet point: A function whose last argument is a function reference can be called with the lambda outside the parentheses. If the function reference is the only argument, you don't need the parentheses at all. It's called "trailing lambda syntax". This feature allows DSL style code to be written, and it is used extensively in Jetpack Compose. This feature also enables cleaner looking use of lambdas:

    myButton.setOnClickListener( (button) -> {
        Log.i(TAG, "Button clicked");
        doSomething();
    });
    

    vs.

    myButton.setOnClickListener {
        Log.i(TAG, "Button clicked")
        doSomething()
    }
    

    And it enables scope functions:

    myLayout.myButton.setTextColor(Color.RED);
    myLayout.myButton.setUseElipsis(true);
    myLayout.myButton.setText("Hello world");
    myLayout.myButton.startBlinkAnimation();
    

    vs.

    myLayout.myButton.apply {
        textColor = Color.RED
        isUseElipsis = true
        text = "Hello world"
        startBlinkAnimation()
    }
    

    Third bullet point: This is trailing lambda syntax again, but with other arguments that come before the lambda, so it still has parentheses.

    Fourth bullet point: Compose does a lot of compiler "magic", so don't think too hard about instances being returned from its methods and garbage collection the way you're used to. Compose collects all the stuff that's being built from @Composable functions and may be holding references to it all under the hood. The compiler plugin uses the DSL to build your actual UI.