Android: Kotlin with Butterknife
Asked Answered
M

10

59

I'm trying to use Kotlin with Butterknife for my Android Application.

Here is my build.gradle

dependencies {
    ...
    compile 'com.jakewharton:butterknife:8.0.1'
    kapt 'com.jakewharton:butterknife-compiler:8.0.1'
}

kapt {
    generateStubs = true
}

I also has an EditText and I want to show a message using ButterKnife when it is changed:

@OnTextChanged(R.id.input)
fun test() {
   toast(1)
}

However, nothing happens. I put a breakpoint into the function - and it is not even executed.

P.S: I have heard about kotterknife, however I have seen an example with pure Butterknife.

What am I doing wrong?

Motte answered 25/2, 2017 at 6:58 Comment(7)
https://mcmap.net/q/330765/-does-butterknife-7-x-work-with-kotlin-m14/2949612Fowliang
@Fowliang did you even read the question? I have exactly the same done in my codeMotte
I know it is not a direct answer but with Kotlin, Anko and Kotlin Android Extensions I don't see any use for ButterKnife.Woermer
Did you call ButterKnife.bind(this)?Gaullist
@JanSlominski unless the OP wants to rewrite the whole application in Kotlin there is still a need to run alongside JavaKroll
better you can use Kotlin extension plugin youtube.com/watch?v=AvMbTY6VrukNiemi
@JanSlominski Can you explain us why with anko we dont need more buttkernife?Pejoration
E
101

There's no need for butterknife in Kotlin. You can directly use the following:

// app:build.gradle file

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "com.example.nikhiljadhav.myapplication"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:26.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.android.support:design:26.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.0'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.0'
}

kapt {
    generateStubs = true
}

// xml layout file

<TextView
    android:id="@+id/tvHello"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    android:layout_marginBottom="8dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:layout_marginTop="8dp"
    app:layout_constraintEnd_toEndOf="parent"
    android:layout_marginEnd="8dp"
    app:layout_constraintStart_toStartOf="parent"
    android:layout_marginStart="8dp" />

<TextView
    android:id="@+id/tvId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    android:layout_marginBottom="8dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:layout_marginTop="8dp"
    app:layout_constraintEnd_toEndOf="parent"
    android:layout_marginEnd="8dp"
    app:layout_constraintStart_toStartOf="parent"
    android:layout_marginStart="8dp" />

<EditText
    android:id="@+id/etDemo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:layout_marginTop="8dp"
    app:layout_constraintEnd_toEndOf="parent"
    android:layout_marginEnd="8dp"
    android:onClick="onClick"
    app:layout_constraintStart_toStartOf="parent"
    android:layout_marginStart="8dp" />

// MainActivity.kt file

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        // use the kotlin property
        tvHello.text="Hi bla bla"
        tvId.text="buubububub"
        //set textcolor  
        tvId.setTextColor(ContextCompat.getColor(this, R.color.colorAccent)) 
        etDemo.hint="nhdodfhfgf"

        tvId.setOnClickListener{ view->
            onClick(view)
        }

        fab.setOnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show()
        }
    }

    fun onClick(view: View) {
        Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
    }

    ...
}

For onTextChangeListner:

etText.addTextChangedListener(object : TextWatcher{
        override fun afterTextChanged(p0: Editable?) {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }

        override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }

        override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }

    }) 
Embrue answered 9/8, 2017 at 13:33 Comment(8)
so how do you use @OnTextChanged with Kotlin Extensions?Zakarias
note from a Fragment you need to call from onViewCreated antonioleiva.com/kotlin-android-extensionsHeavyhearted
what about string, colors, dimensions bindings?Ric
This is super great. Looks like you also need to import the xml file such as: import kotlinx.android.synthetic.main.activity_main.*Geest
This answer could do with more explanation.Taunyataupe
Just FYI, Google does not recommend using synthetic view references anymore. old.reddit.com/r/androiddev/comments/ala9p2/…Typist
@Ric @BindDimen(R.dimen.text_margin) @JvmField var textMargin: Int = 0Xanthophyll
Kotlin syntethics are deprecated from Nov 2020Alarick
C
53

In your app level build.gradle

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-kapt'

kapt {
    generateStubs = true
}

dependencies {
    compile 'com.jakewharton:butterknife:10.2.0'
    kapt 'com.jakewharton:butterknife-compiler:10.2.0'
}

In your Top Level build.gradle

buildscript {
    ext.kotlin_version = '1.3.30'
    repositories {
        jcenter()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

Activity

@BindView(R.id.toolbar)  @JvmField var toolbar: Toolbar? = null

or

@BindView(R.id.toolbar) lateinit var toolbar: Toolbar

Inside OnCreate

ButterKnife.bind(this)
Customs answered 23/8, 2017 at 18:10 Comment(1)
Synthetics rule, but sometimes you are not ready to replace them. Thanks for answering the question.Radioisotope
M
20

Kotlin creators tell on their site that: Kotlin Android Extensions plugin (automatically bundled into the Kotlin plugin in Android Studio) solves the same issue: replacing findViewById with a concise and straightforward code. Consider using it unless you're already using ButterKnife and don't want to migrate.

and e.g.

// Using R.layout.activity_main from the main source set
import kotlinx.android.synthetic.main.activity_main.*

class MyActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView.setText("Hello, world!")
        // Instead of findViewById(R.id.textView) as TextView
    }
}

textView is an extension property for Activity, and it has the same type as declared in activity_main.xml.

Matt answered 29/6, 2017 at 13:41 Comment(2)
can you post a link to itImmune
sure. you can find more details here: kotlinlang.org/docs/tutorials/android-plugin.htmlMatt
P
11

In your gradle:

compile 'com.jakewharton:butterknife:8.8.0'
kapt "com.jakewharton:butterknife-compiler:8.8.0"

In your activity

@BindView(R.id.toolbar)
lateinit var mToolbar: Toolbar

Of course, remember ButterKnife.bind(this) and apply the plugin on the top of your app.gradle apply plugin: 'kotlin-kapt'

Check full example

Full Link: https://github.com/JetBrains/kotlin-examples/tree/master/gradle/android-butterknife

Pejoration answered 16/8, 2017 at 9:31 Comment(0)
S
3

You need to simply add ButterKnife.kt into your source tree from the following link:
https://github.com/JakeWharton/kotterknife
It worked for me.

Seadon answered 27/3, 2018 at 7:43 Comment(1)
Kotter Knife::: Deprecated: This was a terrible idea because it allocates an object for every view reference. Do not use, and do not use anything like it. Use view binding instead.Squirrel
K
2

Add this in your Project Build.gradle

buildscript {
ext.kotlin_version = '1.1.2-4'
ext.butterknife_version = '8.6.0'
repositories {
    maven { url 'https://maven.google.com' }
    jcenter()
}
dependencies {
    classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    classpath "com.jakewharton:butterknife-gradle-plugin:$butterknife_version"

    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
   }
}

And in your app Build.Gradle add this.

    //Butterknife
compile "com.jakewharton:butterknife:$butterknife_version"
kapt "com.jakewharton:butterknife-compiler:$butterknife_version"
Krohn answered 8/6, 2017 at 11:18 Comment(0)
H
1

You can implement some extensions to improve your views behavior. Checkout this example for "onTextChange" in a regular editText:

fun EditText.onTextChange(callback: (text: CharSequence?, start: Int, before: Int, count: Int) -> Unit) {
    addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {}

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            callback(s, start, before, count)
        }
    })
}

Usage:

m_editText.onTextChange { text, _, _, _ -> 
   m_textView.text = text
}

I vote for kotlin-android-extensions

Hakim answered 30/10, 2017 at 20:44 Comment(0)
I
1

In Kotlin, actually there is no need (or) necessity for going ButterKnife concepts. because in your activity you can directly refer the view _id of the layout file as referred below.

layout.xml

<Button
     android:id="@+id/btn_prestage"
     android:layout_width="20dp"
     android:layout_height="20dp"
     android:background="@drawable/prestaging_off"/>

Activity.kt

 btn_prestage.setBackgroundResource(R.drawable.staging_on)
 btn_prestage.setOnClickListener{ view ->
            Snackbar.make(view, "My Action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show() }

build.gradle(app)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
   dependencies {... }
}

kapt {
    generateStubs = true
}
Interbreed answered 15/11, 2017 at 11:32 Comment(0)
W
1

Jake Wharton created new library for kotlin called kotterknife: https://github.com/JakeWharton/kotterknife Gradle:

compile 'com.jakewharton:kotterknife:0.1.0-SNAPSHOT'

View:

val lastName: TextView by bindView(R.id.last_name)

  // Optional binding.
  val details: TextView? by bindOptionalView(R.id.details)

  // List binding.
  val nameViews: List<TextView> by bindViews(R.id.first_name, R.id.last_name)

  // List binding with optional items being omitted.
  val nameViews: List<TextView> by bindOptionalViews(R.id.first_name, R.id.middle_name, R.id.last_name)
Weisler answered 18/10, 2018 at 8:57 Comment(0)
V
1

you can import all synthetic properties for the free/res/layout/activity_main.xml layout by adding this import:

import kotlinx.android.synthetic.main.activity_main.*

Now you can access all views by using there id's no need to initiate findbyid

Verona answered 18/3, 2019 at 7:16 Comment(1)
"synthetic" is deprecated developer.android.com/topic/libraries/view-binding/migrationFishnet

© 2022 - 2024 — McMap. All rights reserved.