Skip to main content

Apply Kotlin DataBinding to Android Studio Generated Main Activity

I posted how to setup Kotlin and DataBinding in Android Stuido in the last blog (http://marksunghunpark.blogspot.com.au/2017/04/kotlin-binding-library-setup-in-android.html). In this post, I am going to how to use DataBiding in the MainActivity when you create a navigation drawer project from Android Studio templates.

Layouts

You will have four layouts in app/src/res/layout folder:
  • app/src/main/res/layout/activity_main.xml
  • app/src/main/res/layout/app_bar_main.xml
  • app/src/main/res/layout/content_main.xml
  • app/src/main/res/layout/nav_header_main.xml
And activity_main.xml contains all other layout using include layout. You need to have tag in activity_main.xml, app_bar_main.xml and content_main.xml. If you don't have the tag, Binding library cannot recognise the sub layouts properly. Binding library doesn't support toolbar and navigation drawer yet, so you can use using BindingAdapter if you want to use binding library.(But I'm gong to skip this part for simplicity)

activity_main.xml

    

    

    

        

        

    

app_bar_main.xml



    
        
    


    

        

            

        

        

        

    

content_main.xml


    

    

    

        
    

Nothing different from when you use Java. Note that you need to give id to access child element in include layout.

Activity

MainActivity.kt
import android.databinding.DataBindingUtil
import android.os.Bundle
import android.support.design.widget.NavigationView
import android.support.design.widget.Snackbar
import android.support.v4.view.GravityCompat
import android.support.v7.app.ActionBarDrawerToggle
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import ocb.app.spark.com.ocb.R
import ocb.app.spark.com.ocb.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {

    val binding: ActivityMainBinding by lazy {
        DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val toolbar = binding.appBarLayout.toolbar
        setSupportActionBar(toolbar)


        binding.appBarLayout.buttonClickListener = View.OnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show()
        }

        val drawer = binding.drawerLayout//findViewById(R.id.drawer_layout) as DrawerLayout
        val toggle = ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
        drawer.setDrawerListener(toggle)
        toggle.syncState()

        val navigationView = binding.navView
        navigationView.setNavigationItemSelectedListener(this)
    }

    override fun onBackPressed() {
        val drawer = binding.drawerLayout //findViewById(R.id.drawer_layout) as DrawerLayout
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START)
        } else {
            super.onBackPressed()
        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        val id = item.itemId


        if (id == R.id.action_settings) {
            return true
        }

        return super.onOptionsItemSelected(item)
    }

    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        // Handle navigation view item clicks here.
        val id = item.itemId

        if (id == R.id.nav_camera) {
            // Handle the camera action
        } else if (id == R.id.nav_gallery) {

        } else if (id == R.id.nav_slideshow) {

        } else if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_share) {

        } else if (id == R.id.nav_send) {

        }

        val drawer = binding.drawerLayout
        drawer.closeDrawer(GravityCompat.START)
        return true
    }
}
Binding library generates a binding class in app/build/generated/source/kapt/debug/ocb/app/your_package/databinding directory and its name is based on the layout file name. The library removes underscore from the layout name and attach 'Binding' as a suffix and makes it upper-camelcase. So the generated binding class name is ActivityMainBinding.
activity_main -> ActivityMainBinding.java
app_bar_main -> AppBarMainBinding.java
content_main -> ContentMainBiding.java
You can use lazy property in Kotlin to bind the layout. binding property will be initialised when it's used. Cool!
val binding: ActivityMainBinding by lazy {
        DataBindingUtil.setContentView(this, R.layout.activity_main)
    }
You can access an element in the include layout through id of it.
val toolbar = binding.appBarLayout.toolbar
setSupportActionBar(toolbar)
To bind onClickListener to the float button in app_bar_main.xml, you can create View.OnClickListener object. Android Binding library doesn't work well with Java8.
binding.appBarLayout.buttonClickListener = View.OnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show()
        }
With Kotlin and DataBinding, we can see our code is much cleaner and short.

Comments

Popular posts from this blog

How to test AsyncTask in Android

In Android, test is not as easy as any other platform. Because Android test cannot be run without emulator. Particulary when it comes to AsyncTask or Service, it is difficult to test because they are different type of thread and hard to check their result. Then, how can we ensure the result of AsyncTask valid? AsyncTask is a thread and an asynchnorous as the name means. So, we need to wait for it finishes its job and need to capture the event. Then, when it happens in AsyncTask. It can be one of onBackground() and onPostExecute() methods. It doesn't matter you use onBackground() or onPostExecute() but I prefer onPostExecute(). Anyway, we can test an AsyncTask if we can hook it. Then, how can we hook it? For that, we can use callback pattern. But we need to delay main thread to wait for the AsyncTask's job done because we want to check the result. So the structure for the test would be like: 1. Create AsyncTask A 2. Injection a callback into A 3. Wait until A finish 4.

Let's start Lambda in Android

In Android programming, I think Java & J2EE programming are not that different, we developers put many boiler plate code particularly when we use anonymous instances. Example1. Simple example button1.setOnClickListener(new OnClickListener(){ @Override public void onClick(View view){ //Do something } }); In the above example, what we really need is inside of onClick() method. Other code is actually decorations and we don't want it. How about we can pass just onClick method body as a parameter of setOnClickListener method like below? button1.setOnClickListener(view->{ //Do something }); That's what we have exactly wanted. We can use the code style in Java 8. I think you already might know about Java 8 Lambda but there is no official support for Java 8 in Android 8. It would be awesome we can use it in Android and there is a way to be able to use Lambda in Android as well. You can refer my previous blog to setup RetroLambda in Andro