Starting from this week, every Monday’s article push will serialize Kotlin’s Android development articles. If you are concerned about the topic, you can directly feedback to me, which can also help me improve the pertinenation of subsequent articles.
1. A thousand-li journey begins with Hello World
Now that we’re stuck with Kotlin, how do we apply it to Android development? As experienced developers, we all know that Android is now built using Gradle. We only need to configure the Kotlin code to compile the Android code.
How difficult is this question? The Kotlin team solved this problem for us by adding:
apply plugin: 'kotlin-android'
Copy the code
This is not the same as the dependencies we add to the Java Virtual Machine. For example, we need to add the dependencies to the Java Virtual Machine.
Buildscript {ext.kotlin_version = '1.0.6'// Version number select repositories {jCenter ()} dependencies {classpath 'com. Android. Tools. Build: gradle: 2.2.0' classpath "org. Jetbrains. Kotlin: kotlin - gradle - plugin: $kotlin_version"}}Copy the code
Add the Kotlin library to your application dependencies:
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
Copy the code
With that, your Kotlin code is ready to run on Android! Of course, you can write your code in SRC /main/ Java or SRC /main/kotlin. It doesn’t matter, I think Java and Kotlin code should be mixed, there’s no need to separate, well, you better not feel like they’re two different languages, that’s all.
package net.println.kotlinandroiddemo
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.TextView
class MainActivity : AppCompatActivity() {
private lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById(R.id.hello) as TextView
textView.text = "Hello World"
}
}
Copy the code
We define a TextView member, and since we can only initialize this member in onCreate, we have to decorate it with LateInit. Of course, if you go to the trouble, you can choose TextView, right? And then initialize the member to NULL.
Then we use the basic findViewById, type coercion to get the reference to the textView, and then setText.
Running naturally is no problem.
But, but! If THIS is all I have to write to get past this week’s articles, I’m probably going to eat a year’s worth of scrambled eggs and tomatoes.
2. Anko is super
When it comes to writing Android with Kotlin, Anko has no idea who doesn’t know who doesn’t know. Well, let’s stop boasting and invite him out at once:
Compile 'org.jetBrains. Anko :anko-sdk15:0.9' // sdk19, sdk21, Sdk23 are also available compile 'org.jetBrains. Anko: ANKo-support-V4 :0.9' // In case you need support-V4 bindings Compile 'org.jetBrains. Anko: Anko-appcompat-V7 :0.9' // For Appcompat-V7 bindingsCopy the code
A quick note about anko-SDK version selection:
-
Org.jetbrains. Anko: anko-sdk15:15 <= minSdkVersion < 19
-
Org.jetbrains. Anko: anko-sdk19:19 <= minSdkVersion < 21
-
Org.jetbrains. Anko: anko-sdk21:21 <= minSdkVersion < 23
-
Org.jetbrains. Anko: anko-sdk23:23 <= minSdkVersion
Of course, in addition to these, Anko also supports cardView, RecyclerView and so on. You can add them as needed. For details, please refer to Github – Anko
In addition, it is recommended that you define the anko library version as a variable, for example:
Ext anko_version = "0.9"... compile "org.jetbrains.anko:anko-sdk15:$anko_version" // sdk19, sdk21, sdk23 are also available compile "org.jetbrains.anko:anko-support-v4:$anko_version" // In case you need support-v4 bindings compile "org.jetbrains.anko:anko-appcompat-v7:$anko_version" // For appcompat-v7 bindingsCopy the code
Okay, so what can we do with Anko?
textView = find(R.id.hello)
Copy the code
Remember findViewById? It becomes find, and strong turns are gone, isn’t that interesting? You must be wondering what Anko did to save so much work, so let’s jump in and see what find really is:
inline fun <reified T : View> View.find(id: Int): T = findViewById(id) as T inline fun <reified T : View> Activity.find(id: Int): T = findViewById(id) as T inline fun <reified T : View> Fragment.find(id: Int): T = view? .findViewById(id) as TCopy the code
First of all, it is an extension method. We are only using the extension version of Activity for now. In fact, views and fragments have this extension method. Second, it is inline and uses the reified generic parameter, which we should have said:
textView = find<TextView>(R.id.hello)
Copy the code
Because the types of generic parameters can be easily deduced, we do not need to specify them explicitly when we use find.
What is reified actually used for? We can also use this method without inline and reified:
fun <T : View> Activity.myFind(id: Int): T = findViewById(id) as T
textView = myFind(R.id.hello)
Copy the code
However, inline saves a function call, and reified also eliminates the IDE’s type checking hint, so why not?
Of course, the benefits of using Anko can not be so little, we hold it down today, if you are curious, you can go to have a look (I ~ know ~ way ~, you certainly can’t help it!!). ~
Don’t the findViewById (3)
As the first article about Kotlin writing Android, the kotlin-Android-Extensions extension is a must. Add configuration to Gradle:
apply plugin: 'kotlin-android-extensions'
Copy the code
After that, we just need to use the textView with the ID Hello defined in the layout directly in the Activity code, so:
Import android. OS. Bundle import android. Support the v7. App. AppCompatActivity / / this package will automatically import import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {super.oncreate (savedInstanceState) setContentView(r.layout.activity_main) Hello is actually the view's id in the layout hello.text = "hello World"}}Copy the code
As long as you add a View to your layout, you can use the ID to refer to the View in your Activity, View, or Fragment
So, aren’t you going to ask me why? Why can we do that?
To answer this question, Android Studio needs to be able to index the Hello View from the IDE level, which requires the support of Kotlin’s IDE plug-in (don’t ask me what an IDE plug-in is, you probably installed it on your first day with Kotlin). Second, at compile time, the compiler can find the hello variable, which requires Kotlin’s Gradle plugin to support. . Knowing these two points, we will have a target
“Ah!” There Kotlin source a scream…
high energy alert The source code we discussed is mainly in the Android-extensions-Compiler and Android-extensions-idea modules in the plugins directory.
If you were to implement your own mechanism to do this, you would probably think, well, I’ll have to parse the XML layout file first and save the View in it for later lookup. I’m telling you, Kotlin did the same thing!
AndroidXmlVisitor.kt
override fun visitXmlTag(tag: XmlTag?) {... val idAttribute = tag? .getAttribute(AndroidConst.ID_ATTRIBUTE) if (idAttribute ! = null) { val idAttributeValue = idAttribute.value if (idAttributeValue ! = null) { val xmlType = tag? .getAttribute(AndroidConst.CLASS_ATTRIBUTE_NO_NAMESPACE)? .value ? : localName val name = androidIdToName(idAttributeValue) if (name ! = null) elementCallback(name, xmlType, idAttribute) } } tag? .acceptChildren(this) }Copy the code
This is code that iterates through XML tags, typical visitor pattern, right? If you get this tag, it has the android: ID attribute, so small, you don’t leave, tell the truth what your ID is! For example, if the tag looks like this:
<Button
android:id="@+id/login"
... />
Copy the code
So, name is login, and since name is not empty, calling elementCallback is essentially logging it.
IDEAndroidLayoutXmlFileManager.kt
override fun doExtractResources(files: List<PsiFile>, module: ModuleDescriptor): List<AndroidResource> {val widgets = arrayListOf<AndroidResource>() // Notice that the Lambda expression is the elementCallback val visitor = AndroidXmlVisitor { id, widgetType, attribute -> widgets += parseAndroidResource(id, widgetType, Attribute.valueelement)} files.foreach {it. Accept (visitor)}Copy the code
Now that we’ve found all the views with ids in the layout, we need to make our activities find those views somehow, and we find that we always import a package called:
Kotlinx. Android. Synthetic. Main. < filename > layout. *Copy the code
How many meanings? The Kotlin compiler created a package for us, right?
AndroidPackageFragmentProviderExtension.kt
. createPackageFragment(packageFqName, false) createPackageFragment(packageFqName + ".view", true) ...Copy the code
Notice that the packageFqName is actually the one we mentioned earlier
Kotlinx. Android. Synthetic. Main. < filename > layoutCopy the code
Create two packages. We import the first package in our Activity, but we use the second package when we refer to the child view with the parent view:
. import kotlinx.android.synthetic.main.activity_main.view.* class OverlayManager(context: Context){ init { val view = LayoutInflater.from(context).inflate(R.layout.activity_main, null) view.hello.text = "HelloWorld" ... }... }Copy the code
Well, we now know that IntelliJ has helped us sneak in two virtual packages by parsing THE XML so that we can easily explain the reference to the package in our code.
This time may be some more doubts click Activity after hello how to jump to XML, the everyone read the AndroidGotoDeclarationHandler source will be very easy to see the answer.
With all this space, we’ve only scratched the surface. Everything above is actually a smoke screen, anyway, these two packages are virtual, how to compile?
FindViewById: hello.text = “HelloWorld”; findViewById: hello.text = “HelloWorld”;
L2 LINENUMBER 12 L2 ALOAD 0 GETSTATIC net/println/kotlinandroiddemo/R$id.hello : I INVOKEVIRTUAL net/println/kotlinandroiddemo/MainActivity._$_findCachedViewById (I)Landroid/view/View; CHECKCAST android/widget/TextView LDC "Hello World" CHECKCAST java/lang/CharSequence INVOKEVIRTUAL android/widget/TextView.setText (Ljava/lang/CharSequence;) VCopy the code
How does this work? Please read AndroidExpressionCodegenExtension kt,
. //GETSTATIC net/println/kotlinandroiddemo/R$id.hello : I v.getstatic(packageName.replace(".", "/") + "/R\$id", resourceId.name, "I") //INVOKEVIRTUAL net/println/kotlinandroiddemo/MainActivity._$_findCachedViewById (I)Landroid/view/View; v.invokevirtual(declarationDescriptorType.internalName, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;" , false) ...Copy the code
Well, at this point, you can have a thorough understanding of Android’s HelloWorld code.
4. Summary
It’s HelloWorld, but it’s not that easy to figure out all the secrets, and most of the time, reading Kotlin’s source code is almost the only way.
Thank you for your attention and support. If you have any questions, please contact me